classdef GlomViewer < handle
    % GlomViewer - Xglom: Virtual Microscopy Explorer Application
    %
    %   GlomViewer is an interactive virtual microscopy imaging tool
    %   for identifying glomeruli sizes and distributions.
    %
    %   GlomViewer(DarisDirectory) starts the GUI with one argument as the source
    %   directory containing DaRIS formatted tiff directories.
    %
    %   The GUI allows you to navigate through different slides and process
    %   image files. Default features of the image viewer users to interact with the images by
    %   panning (clicking and drag), zooming (right-click and drag),
    %   centering view (double-clicking).  Processing of slides is performed by holding down the
    %   shift key then clicking to create a new glomeruli location
    %
    
    %     Copyright © 2012-2013 Michael Eager <michael.eager@monash.edu>
    %     Monash Biomedical Imaging, Monash University, Australia
    %
    %     This file is part of Xglom.
    %
    %     This is free software: you can redistribute it and/or modify
    %     it under the terms of the GNU General Public License as published by
    %     the Free Software Foundation, either version 3 of the License, or
    %     (at your option) any later version.
    %
    %     This is distributed in the hope that it will be useful,
    %     but WITHOUT ANY WARRANTY; without even the implied warranty of
    %     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    %     GNU General Public License for more details.
    %
    %     You should have received a copy of the GNU General Public License
    %     along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    
    %
    %   The author wishes to acknowledge this work is based on ImageViewer
    %   (BSD licence, Copyright 2006-2012 The MathWorks, Inc).
    % VERSIONS:
    %   v1.0 - first version. (was pictureviewer.m)
    %   v1.1 - convert to nested functions. (Nov 13, 2006)
    %   v1.2 - bug fix to deal with different image types (Nov 15, 2006)
    %   v1.3 - bug fix for centering, sorting of image files.
    %          add resize window feature.
    %          change FINDOBJ to FINDALL (Nov 16, 2006)
    %   v1.4 - cosmetic changes to the GUI.
    %          a better timer management. (Dec 2, 2006)
    %   v1.5 - added scroll wheel zoom functionality. This only works with
    %          R2007a or later. (Jan 10, 2007)
    %   v1.51 - add white background under red zoom line (June 2007)
    %   v2.0 - Implemented in MATLAB Classes. (Aug 2012)
    %
    % Jiro Doke
    % April 2006
    %
    %   v3.0 - GlomViewer takes ImageViewer as a template to create a new
    %          GUI for viewing and data collection of Virtual Microscopy
    %          images of kidneys.
    %   v3.1 - Updated to view/load/manipulate DaRIS formatted VM slides
    %          in subdirectories. New features include adding markers to
    %          identify glomeruli, adding a grid over the image, and the
    %          ability to mark cross-over points as tuft/capsule/nothing.
    %   v3.2 - Usability features added for user friendliness.  Help guide
    %          file info, and data display updated.
    %
    % Michael Eager (michael.eager@monash.edu)
    % 2013 Monash Biomedical Imaging
    
    properties (Hidden, Access=protected)
        handles
    end
    % properties (Access=public) %SetObservable)
    %    Data=struct();
    %    DatasetVariables=struct();
    % end
    properties (Constant, Hidden, Access=protected)
        closedHandPointer = [
            NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN
            NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN
            NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN
            NaN,NaN,NaN,NaN,2  ,2  ,NaN,2  ,2  ,NaN,2  ,2  ,NaN,NaN,NaN,NaN
            NaN,NaN,NaN,2  ,1  ,1  ,2  ,1  ,1  ,2  ,1  ,1  ,2  ,2  ,NaN,NaN
            NaN,NaN,2  ,1  ,2  ,2  ,1  ,2  ,2  ,1  ,2  ,2  ,1  ,1  ,2  ,NaN
            NaN,NaN,2  ,1  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,1  ,2  ,1  ,2
            NaN,NaN,NaN,2  ,1  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,1  ,2
            NaN,NaN,2  ,1  ,1  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,1  ,2
            NaN,2  ,1  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,1  ,2
            NaN,2  ,1  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,1  ,2
            NaN,2  ,1  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,1  ,2  ,NaN
            NaN,NaN,2  ,1  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,1  ,2  ,NaN
            NaN,NaN,NaN,2  ,1  ,2  ,2  ,2  ,2  ,2  ,2  ,2  ,1  ,2  ,NaN,NaN
            NaN,NaN,NaN,NaN,2  ,1  ,2  ,2  ,2  ,2  ,2  ,2  ,1  ,2  ,NaN,NaN
            NaN,NaN,NaN,NaN,2  ,1  ,2  ,2  ,2  ,2  ,2  ,2  ,1  ,2  ,NaN,NaN
            ];
        
        zoomInOutPointer = [
            NaN,NaN,NaN,2  ,2  ,NaN,NaN,NaN,NaN,NaN,NaN,2  ,2  ,NaN,NaN,NaN
            NaN,NaN,2  ,1  ,1  ,2  ,NaN,NaN,NaN,NaN,2  ,1  ,1  ,2  ,NaN,NaN
            NaN,2  ,1  ,1  ,1  ,1  ,2  ,NaN,NaN,2  ,2  ,1  ,1  ,2  ,2  ,NaN
            2  ,1  ,1  ,1  ,1  ,1  ,1  ,2  ,2  ,1  ,1  ,1  ,1  ,1  ,1  ,2
            2  ,1  ,2  ,1  ,1  ,2  ,1  ,2  ,2  ,1  ,1  ,1  ,1  ,1  ,1  ,2
            NaN,2  ,2  ,1  ,1  ,2  ,2  ,NaN,NaN,2  ,2  ,1  ,1  ,2  ,2  ,NaN
            NaN,NaN,2  ,1  ,1  ,2  ,NaN,NaN,NaN,NaN,2  ,1  ,1  ,2  ,NaN,NaN
            NaN,NaN,NaN,2  ,2  ,NaN,NaN,NaN,NaN,NaN,NaN,2  ,2  ,NaN,NaN,NaN
            NaN,NaN,NaN,2  ,2  ,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN
            NaN,NaN,2  ,1  ,1  ,2  ,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN
            NaN,2  ,2  ,1  ,1  ,2  ,2  ,NaN,NaN,2  ,2  ,2  ,2  ,2  ,2  ,NaN
            2  ,1  ,2  ,1  ,1  ,2  ,1  ,2  ,2  ,1  ,1  ,1  ,1  ,1  ,1  ,2
            2  ,1  ,1  ,1  ,1  ,1  ,1  ,2  ,2  ,1  ,1  ,1  ,1  ,1  ,1  ,2
            NaN,2  ,1  ,1  ,1  ,1  ,2  ,NaN,NaN,2  ,2  ,2  ,2  ,2  ,2  ,NaN
            NaN,NaN,2  ,1  ,1  ,2  ,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN
            NaN,NaN,NaN,2  ,2  ,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN
            ];
        
    end
    %     events % Define an event called StateChange
    %       StateChange
    %     end
    methods
        function obj = GlomViewer(dirname)
            
            verNumber = '3.2';
            using_darisdir=0;
            if nargin == 0
                dirname = pwd;
                
            else
                if ~ischar(dirname) || ~isdir(dirname)
                    error('Invalid input argument.\n  Syntax: %s(DIRNAME)', upper(mfilename));
                end
                if exist(fullfile(dirname,'info.txt'),'file')
                    using_darisdir=1;
                end
            end
            
%% Check memory size, minimum is 15 GB of RAM
if(java.lang.Runtime.getRuntime.maxMemory/(1024*1024*1024) < 15 ) 
    warndlg('The maximum memory allocation is insufficient for Xglom.  Go to File->Preferences, then to section General->Jave Heap Memory to increase memory allocation. Recommended maximum heap memory size is 25 GB (25000 MB). You must close Xglom and MATLAB then restart. Press OK to continue.','!! Warning Low Memory !!')
end
            
            
            delete(findall(0, 'type', 'figure', 'tag', 'GlomViewer'));
            
            bgcolor1 = [.8 .8 .8];
            txtcolor = [.3 .3 .3];
            
            fig_logo = figure(...
                'units'                         , 'normalized', ...
                'busyaction'                    , 'queue', ...
                'resize'                        , 'off', ...
                'toolbar'                       , 'none', ...
                'menubar'                       , 'none', ...
                'visible'                       , 'on',...
                'tag'                           , 'GlomViewerPre');
            imshow('kidney.png');
            title(' Loading GlomViewer: Xglom''s VM GUI',...
                'HorizontalAlignment','center',...
                'FontWeight','bold',...
                'FontSize',16);
            
            figH = figure(...
                'units'                         , 'normalized', ...
                'busyaction'                    , 'queue', ...
                'color'                         , bgcolor1, ...
                'deletefcn'                     , @obj.stopTimerFcn, ...
                'doublebuffer'                  , 'on', ...
                'handlevisibility'              , 'callback', ...
                'interruptible'                 , 'on', ...
                'menubar'                       , 'none', ...
                'name'                          , upper(mfilename), ...
                'numbertitle'                   , 'off', ...
                'outerposition'                 , [.05 .05 .9 .9], ...
                'resize'                        , 'on', ...
                'resizefcn'                     , @obj.resizeFcn, ...
                'tag'                           , 'GlomViewer', ...
                'toolbar'                       , 'none', ...
                'visible'                       , 'off', ...
                'defaultaxesunits'              , 'pixels', ...
                'defaulttextfontunits'          , 'pixels', ...
                'defaultuicontrolunits'         , 'pixels', ...
                'defaultuicontrolfontunits'     , 'pixels', ...
                'defaultuicontrolinterruptible' , 'off');
            %                'defaultuicontrolfontsize'      , 10, ...
            %                'defaultuicontrolfontname'      , 'Verdana', ...
            %                'defaulttextfontname'           , 'Verdana', ...
            %                'defaulttextfontsize'           , 12, ...
            
            
            try
                if ~verLessThan('matlab', '7.4')
                    set(figH, 'windowscrollwheelfcn', @obj.scrollWheelFcn);
                end
            catch %#ok<CTCH>
                % verLessThan was introduced in v7.4
                % if there is an error, that means it is less than 7.4
            end
            
            uph(1) = uipanel(...
                'units'                     , 'pixels', ...
                'backgroundcolor'           , bgcolor1, ...
                'parent'                    , figH, ...
                'bordertype'                , 'beveledin', ...
                'tag'                       , 'versionPanel');
            uicontrol(...
                'style'                     , 'text', ...
                'foregroundcolor'           , txtcolor, ...
                'backgroundcolor'           , bgcolor1, ...
                'horizontalalignment'       , 'center', ...
                'fontweight'                , 'bold', ...
                'string'                    , sprintf('Ver %s', verNumber), ...
                'parent'                    , uph(1), ...
                'tag'                       , 'versionText',...
                'tooltipstring',    ['The version of GlomViewer is ' sprintf('Ver %s', verNumber)]);
            uph(2) = uipanel(...
                'units'                     , 'pixels', ...
                'backgroundcolor'           , bgcolor1, ...
                'parent'                    , figH, ...
                'bordertype'                , 'beveledin', ...
                'tag'                       , 'statusPanel');
            uicontrol(...
                'style'                     , 'text', ...
                'foregroundcolor'           , txtcolor, ...
                'backgroundcolor'           , bgcolor1, ...
                'horizontalalignment'       , 'right', ...
                'fontweight'                , 'bold', ...
                'string'                    , '', ...
                'parent'                    , uph(2), ...
                'tag'                       , 'statusText');
            uph(3) = uipanel(...
                'units'                     , 'pixels', ...
                'bordertype'                , 'etchedout', ...
                'fontweight'                , 'bold', ...
                'title'                     , 'View', ...
                'titleposition'             , 'centertop', ...
                'backgroundcolor'           , bgcolor1, ...
                'parent'                    , figH, ...
                'tag'                       , 'frame1');
            %                'fontname'                  , 'Verdana', ...
            uicontrol(...
                'style'                     , 'edit', ...
                'string'                    , '', ...
                'horizontalalignment'       , 'center', ...
                'fontweight'                , 'bold', ...
                'fontsize'                  , 12, ...
                'callback'                  , @obj.resetView, ...
                'tooltipstring'             , 'Edit Zoom Scale', ...
                'backgroundcolor'           , bgcolor1, ...
                'foregroundcolor'           , [.2, .2, .2], ...
                'parent'                    , uph(3), ...
                'tag'                       , 'ZoomCaptionText');
            uicontrol(...
                'style'                     , 'pushbutton', ...
                'string'                    , 'Full', ...
                'fontweight'                , 'bold', ...
                'callback'                  , @obj.resetView, ...
                'enable'                    , 'off', ...
                'tooltipstring'             , 'View full image', ...
                'parent'                    , uph(3), ...
                'tag'                       , 'ResetViewBtn1');
            uicontrol(...
                'style'                     , 'pushbutton', ...
                'string'                    , 'Custom', ...
                'fontweight'                , 'bold', ...
                'callback'                  , @obj.resetView, ...
                'buttondownfcn'             , @obj.resetView, ...
                'enable'                    , 'off', ...
                'tooltipstring'             , 'Custom: 100%', ...
                'parent'                    , uph(3), ...
                'tag'                       , 'ResetViewBtn2');
            uicontrol(...
                'style'                     , 'pushbutton', ...
                'string'                    , 'Help', ...
                'fontweight'                , 'bold', ...
                'callback'                  , @helpBtnCallback, ...
                'enable'                    , 'on', ...
                'parent'                    , figH, ...
                'tooltipstring'             , 'Show GlomViewer help dialog', ...
                'tag'                       , 'HelpBtn');
            %% ------------ Grid Frame----------------------
            uph(4) = uipanel(...
                'units'                     , 'pixels', ...
                'bordertype'                , 'etchedout', ...
                'fontweight'                , 'bold', ...
                'title'                     , 'Grid', ...
                'titleposition'             , 'centertop', ...
                'backgroundcolor'           , bgcolor1, ...
                'parent'                    , figH, ...
                'tag'                       , 'framegrid');
            uicontrol(...
                'style'                     , 'listbox', ...
                'backgroundcolor'           , 'white', ...
                'callback'                  , @obj.GridSpacingBoxCallback, ...
                'fontname'                  , 'FixedWidth', ...
                'parent'                    , uph(4), ...
                'tag'                       , 'GridSpacingBox', ...
                'tooltipstring'             , '<html><b>Grid spacing menu</b><br />Change the grid spacing using pre-defined settings for mouse, rat or human. Use <b>custom</b> for alternate grid spacing</html>', ...
                'string' , {'Mouse Scale (25 um)',...
                'Rat Scale (35 um)',...
                'Human Scale (45 um)', ...
                'Custom'});
            uicontrol(...
                'style'                     , 'text', ...
                'backgroundcolor'           , bgcolor1, ...
                'fontname'                  , 'FixedWidth', ...
                'parent'                    , uph(4), ...
                'tag'                       , 'GridTextBox',...
                'string'                    , 'Spacing (um)');
            uicontrol(...
                'callback'                  , @obj.GridValueBoxCallback, ...
                'style'                     , 'edit', ...
                'backgroundcolor'           , 'white', ...
                'fontname'                  , 'FixedWidth', ...
                'parent'                    , uph(4), ...
                'tag'                       , 'GridValueBox',...
                'string'                    , '25');
            %
            uicontrol(...
                'style'                     , 'togglebutton', ...
                'string'                    , 'State: OFF', ...
                'tooltipstring'             , '<html>Toggle grid <b>ON</b> or <b>OFF</b></html>', ...
                'fontsize'                  , 14,...
                'fontweight'                , 'bold', ...
                'callback'                  , @obj.gridBtnCallback, ...
                'enable'                    , 'on', ...
                'parent'                    , uph(4), ...
                'tag'                       , 'GridBtn');
            
            %% ------------Saving buttons----------------
            uicontrol(...
                'style'                     , 'pushbutton', ...
                'string'                    , 'Refresh', ...
                'fontweight'                , 'bold', ...
                'callback'                  , @obj.RefreshBtnCallback, ...
                'enable'                    , 'on', ...
                'parent'                    , figH, ...
                'tag'                       , 'RefreshBtn');
            uicontrol(...
                'style'                     , 'pushbutton', ...
                'string'                    , 'Save Slide Data', ...
                'fontweight'                , 'bold', ...
                'callback'                  , @obj.SaveBtnCallback, ...
                'enable'                    , 'on', ...
                'parent'                    , figH, ...
                'tag'                       , 'SaveBtn');
            uicontrol(...
                'style'                     , 'pushbutton', ...
                'string'                    , 'Export Data', ...
                'fontweight'                , 'bold', ...
                'callback'                  , @obj.SaveCSVBtnCallback, ...
                'enable'                    , 'on', ...
                'parent'                    , figH, ...
                'tag'                       , 'SaveCSVBtn');
            uicontrol(...
                'style'                     , 'pushbutton', ...
                'string'                    , 'Reload Slide Data', ...
                'fontweight'                , 'bold', ...
                'callback'                  , @obj.LoadBtnCallback, ...
                'enable'                    , 'on', ...
                'parent'                    , figH, ...
                'tag'                       , 'LoadBtn');
            
            %%  --- Data struct and Glom data XXX ---
            uicontrol(...
                'style'                     , 'pushbutton', ...
                'string'                    , 'Show Slide Data', ...
                'fontweight'                , 'bold', ...
                'callback'                  , @obj.ShowDataStructBtnCallback, ...
                'enable'                    , 'on', ...
                'parent'                    , figH, ...
                'tag'                       , 'ShowDataStructBtn');
            uicontrol(...
                'style'                     , 'togglebutton', ...
                'string'                    , 'Show Cur Glom Data', ...
                'fontweight'                , 'bold', ...
                'callback'                  , @obj.ShowGlomDataBtnCallback, ...
                'enable'                    , 'on', ...
                'parent'                    , figH, ...
                'tag'                       , 'ShowGlomDataBtn');
            
            
            
            %% --- Delete buttons ---
            uicontrol(...
                'style'                     , 'pushbutton', ...
                'string'                    , 'Clear Cur Glom', ...
                'fontweight'                , 'bold', ...
                'callback'                  , @obj.DeleteCurrentGlom, ...
                'enable'                    , 'on', ...
                'parent'                    , figH, ...
                'tag'                       , 'DeleteCurrentGlomBtn');
            uicontrol(...
                'style'                     , 'pushbutton', ...
                'string'                    , 'Delete Grid Points', ...
                'fontweight'                , 'bold', ...
                'callback'                  , @obj.DeleteCurrentGlomGridPoints, ...
                'enable'                    , 'on', ...
                'parent'                    , figH, ...
                'tag'                       , 'DeleteGridPointsBtn');
            uicontrol(...
                'style'                     , 'pushbutton', ...
                'string'                    , 'Delete All Grid Points', ...
                'fontweight'                , 'bold', ...
                'callback'                  , @obj.DeleteAllCurrentSlideGridPoints, ...
                'enable'                    , 'on', ...
                'parent'                    , figH, ...
                'tag'                       , 'DeleteAllGridPointsBtn');
            
            %% ---- FileInfo---
            
            uicontrol(...
                'style'                     , 'togglebutton', ...
                'string'                    , 'File Info', ...
                'fontweight'                , 'bold', ...
                'callback'                  , @obj.fileInfoBtnCallback, ...
                'enable'                    , 'off', ...
                'parent'                    , figH, ...
                'tag'                       , 'FileInfoBtn');
            
            
            
            %% ----------Directory frame-----
            uph(5) = uipanel(...
                'units'                     , 'pixels', ...
                'backgroundcolor'           , bgcolor1, ...
                'parent'                    , figH, ...
                'bordertype'                , 'beveledin', ...
                'tag'                       , 'CurrentDirectoryPanel');
            uicontrol(...
                'style'                     , 'text', ...
                'backgroundcolor'           , bgcolor1, ...
                'horizontalalignment'       , 'left', ...
                'parent'                    , uph(5), ...
                'tag'                       , 'CurrentDirectoryEdit');
            uicontrol(...
                'style'                     , 'pushbutton', ...
                'string'                    , '...', ...
                'backgroundcolor'           , bgcolor1, ...
                'callback'                  , @obj.chooseDirectoryCallback, ...
                'parent'                    , figH, ...
                'enable'                    , 'off', ...
                'tag'                       , 'ChooseDirectoryBtn');
            
            % Up Directory Icon
            map = [0 0 0;bgcolor1;1 1 0;1 1 1];
            upDirIcon = uint8([
                1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
                1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
                1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1
                1 1 0 3 2 3 2 3 0 1 1 1 1 1 1 1
                1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
                1 0 2 3 2 3 2 3 2 3 2 3 2 3 2 0
                1 0 3 2 3 2 0 2 3 2 3 2 3 2 3 0
                1 0 2 3 2 0 0 0 2 3 2 3 2 3 2 0
                1 0 3 2 0 0 0 0 0 2 3 2 3 2 3 0
                1 0 2 3 2 3 0 3 2 3 2 3 2 3 2 0
                1 0 3 2 3 2 0 2 3 2 3 2 3 2 3 0
                1 0 2 3 2 3 0 0 0 0 0 3 2 3 2 0
                1 0 3 2 3 2 3 2 3 2 3 2 3 2 3 0
                1 0 2 3 2 3 2 3 2 3 2 3 2 3 2 0
                1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
                1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
                ]);
            rgbIcon = ind2rgb(upDirIcon, map);
            
            uicontrol(...
                'style'                     , 'pushbutton', ...
                'cdata'                     , rgbIcon, ...
                'backgroundcolor'           , bgcolor1, ...
                'callback'                  , @obj.upDirectoryCallback, ...
                'parent'                    , figH, ...
                'enable'                    , 'off', ...
                'tag'                       , 'UpDirectoryBtn');
            
            %% File list and data viewer panels
            uicontrol(...
                'style'                     , 'listbox', ...
                'backgroundcolor'           , 'white', ...
                'callback'                  , @obj.fileListBoxCallback, ...
                'fontname'                  , 'FixedWidth', ...
                'parent'                    , figH, ...
                'tooltipstring'             , '<html><b>File list</b><br /> List of image files in directory.  Directories formatted in Daris TIFF stack format, will show individual slides as directories ''[\<Slide number\>_01_01]''. <br /> Double-click or press enter to load image. </html>', ...
                'tag'                       , 'FileListBox');
            
            uicontrol(...
                'tooltipstring'             , '<html><b>Slide Data Display</b><br /> List of internal variabels pertaining to the image shown in the image axis. Use the <b>Refresh</b> to ensure the data is correct. </html>', ...
                'style'                     , 'listbox', ...
                'backgroundcolor'           , 'white', ...
                'parent'                    , figH, ...
                'tag'                       , 'DataTreeBox');
            %               'callback'                  , @obj.DataTreeBoxCallback, ...
            
            %% ---- Preview frame-------------
            uph(6) = uipanel(...
                'units'                     , 'pixels', ...
                'bordertype'                , 'etchedout', ...
                'backgroundcolor'           , bgcolor1, ...
                'fontweight'                , 'bold', ...
                'title'                     , 'Preview', ...
                'titleposition'             , 'centertop', ...
                'parent'                    , figH, ...
                'tag'                       , 'PreviewImagePanel');
            %               'fontname'                  , 'Verdana', ...
            %               'fontsize'                  , 10, ...
            %                 'tooltipstring'             , '<html><b>Preview of image</b><br /> Double-click or press enter to load image. </html>', ...
            
            axes(...
                'handlevisibility'          , 'callback', ...
                'parent'                    , uph(6), ...
                'visible'                   , 'off', ...
                'tag'                       , 'PreviewImageAxes');
            %% ---- Main Image frame-------------
            axes(...
                'box'                       , 'on', ...
                'xtick'                     , [], ...
                'ytick'                     , [], ...
                'handlevisibility'          , 'callback', ...
                'parent'                    , figH, ...
                'tag'                       , 'ImageAxes');
            
            
            
            % for drawing the zoom line
            axH = axes(...
                'units'                     , 'normalized', ...
                'position'                  , [0 0 1 1], ...
                'box'                       , 'on', ...
                'hittest'                   , 'off', ...
                'xlim'                      , [0 1], ...
                'xtick'                     , [], ...
                'ylim'                      , [0 1], ...
                'ytick'                     , [], ...
                'handlevisibility'          , 'callback', ...
                'visible'                   , 'off', ...
                'parent'                    , figH, ...
                'tag'                       , 'InvisibleAxes');
            
            line(NaN, NaN, ...
                'linestyle'                 , '-', ...
                'linewidth'                 , 3, ...
                'color'                     , 'w', ...
                'parent'                    , axH, ...
                'tag'                       , 'ZoomLine');
            line(NaN, NaN, ...
                'linestyle'                 , '--', ...
                'linewidth'                 , 2, ...
                'color'                     , 'r', ...
                'parent'                    , axH, ...
                'tag'                       , 'ZoomLine');
            
            
            uicontrol(...
                'style'                     , 'listbox', ...
                'backgroundcolor'           , [.75, .75, 1], ...
                'fontname'                  , 'FixedWidth', ...
                'fontsize'                  , 14, ...
                'visible'                   , 'off', ...
                'enable'                    , 'inactive', ...
                'interruptible'             , 'off', ...
                'busyaction'                , 'queue', ...
                'horizontalalignment'       , 'left', ...
                'parent'                    , figH, ...
                'tag'                       , 'MessageTextBox');
            
            
            uicontrol(...
                'style'                     , 'listbox', ...
                'backgroundcolor'           , [.75, .75, 1], ...
                'fontname'                  , 'FixedWidth', ...
                'fontsize'                  , 14, ...
                'visible'                   , 'off', ...
                'enable'                    , 'inactive', ...
                'interruptible'             , 'off', ...
                'busyaction'                , 'queue', ...
                'horizontalalignment'       , 'left', ...
                'parent'                    , figH, ...
                'tag'                       , 'DataTreeContentBox');
            
            %% ----------User Directory frame-----
            uph(7) = uipanel(...
                'units'                     , 'pixels', ...
                'backgroundcolor'           , bgcolor1, ...
                'parent'                    , figH, ...
                'bordertype'                , 'beveledin', ...
                'tag'                       , 'UserDirectoryPanel');
            uicontrol(...
                'style'                     , 'text', ...
                'backgroundcolor'           , bgcolor1, ...
                'horizontalalignment'       , 'left', ...
                'parent'                    , uph(5), ...
                'visible','off',...
                'tooltipstring'             , '<html><b>User Data Storage Directory</b></html>:', ...
                'tag'                       , 'UserDirectoryEdit');
            uicontrol(...
                'style'                     , 'pushbutton', ...
                'string'                    , '...', ...
                'backgroundcolor'           , bgcolor1, ...
                'callback'                  , @obj.chooseUserDirectoryCallback, ...
                'parent'                    , figH, ...
                'enable'                    , 'off', ...
                'visible','on',...
                'tag'                       , 'UserDirectoryBtn');
            
            % addlistener(obj,'StateChange',@obj.StateChangeCallback)
            
            obj.handles             = guihandles(figH);
            obj.handles.figPos      = [];
            obj.handles.axPos       = [];
            obj.handles.debug = 0;
            obj.handles.lastDir     = dirname;
            obj.handles.UserDirectory = dirname;
            obj.handles.GridMode    = 0;
            obj.handles.MovingLabel=0;
            obj.handles.DarisDirMode = using_darisdir;
            obj.handles.ImX   = [];
            obj.handles.CurrentImval=1;
            obj.handles.CachedImX          = {};
            obj.handles.iminfo = [];
            obj.handles.CachedImInfo = {};
            obj.handles.tm          = timer(...
                'name'            , 'image preview timer', ...
                'executionmode'   , 'fixedspacing', ...
                'objectvisibility', 'off', ...
                'taskstoexecute'  , inf, ...
                'period'          , 0.001, ...
                'startdelay'      , 3, ...
                'timerfcn'        , @obj.getPreviewImages);
            
            set(obj.handles.GlomViewer, 'Position', get(0,'ScreenSize'));
            
            
            set(obj.handles.DataTreeBox,'Value',[]);
            set(obj.handles.DataTreeContentBox,'Value',[]);
            
            %obj.Data = struct();
            obj.handles.CurrentSlideDir='0001_01_01';
            
            %% Example DATA struct
            obj.handles.Data.DarisID = '1008.2.18.6.1.1.3';
            obj.handles.Data.Directory = './Cache/1008.2.18.5.1.1.1/0001_01_01';
            obj.handles.Data.Filename = '0001.tif';
            obj.handles.Data.Info=[];
            obj.handles.Data.GridSpacingUM = 35;
            obj.handles.Data.SliceNumber=1;
            obj.handles.Data.NumGloms=2;
            obj.handles.Data.MaxLabel=2;
            
            obj.handles.Data.Glom(1).Label='#000001';
            obj.handles.Data.Glom(1).Location =[1 1];
            obj.handles.Data.Glom(1).TuftGridPoints = [1 1; 2 2];
            obj.handles.Data.Glom(1).CapsuleGridPoints=[1 1; 2 2];
            
            obj.handles.Data.Glom(2).Label='#000002';
            obj.handles.Data.Glom(2).Location =[1 1];
            obj.handles.Data.Glom(2).TuftGridPoints = [1 1; 2 2];
            obj.handles.Data.Glom(2).CapsuleGridPoints=[1 1; 2 2];
            
            
            %% DatasetVariables variables
            obj.handles.DatasetVariables.PixelResolution = 0.005;
            obj.handles.DatasetVariables.SliceSeperation = 0.01;
            
            obj.handles.struct_names = [];
            obj.handles.struct_values =[];
            
            %% Load information from info.txt or info.mat
            % in the directory of the dataset
            obj= loadGlobalVariables(obj,dirname);
            InitDataBox(obj);
            %UpdateDataTreeBox(obj,cell_struct)
            
            
            delete(findall(0, 'type', 'figure', 'tag', 'GlomViewerPre'));
            
            resizeFcn(obj);
            
            % Show initial directory
            obj = showDirectory(obj);
            set(figH, 'visible', 'on');
            
            
            
            
        end
        
        %% loadGlobalVariables
        %   In Daris dataset directory, gather information from info.txt
        %   file
        function obj = loadGlobalVariables(obj,dirname)
            if nargin < 2
                dirname=obj.handles.lastDir;
            end
            disp(['loadGlobalVariables:' dirname]);
            
            if isdir(dirname) && exist(fullfile(dirname,'info.mat'),'file')
                load(fullfile(dirname,'info.mat'), 'GlobalVariables');
                obj.handles.DatasetVariables=GlobalVariables;
                set(obj.handles.GridValueBox,'string', num2str(obj.handles.DatasetVariables.GridSpacingUM));
            end
            if isdir(dirname) && exist(fullfile(dirname,'info.txt'),'file')
                data_cell_string = fileread(fullfile(dirname,'info.txt'));
                data_cell_string = regexp(data_cell_string,'\s+','split');
                
                for i=1:length(data_cell_string)
                    if ~isempty(strfind(data_cell_string{i},'NZ='))
                        obj.handles.DatasetVariables.NumSlides =  str2double(data_cell_string{i}(4:end));
                    end
                    if ~isempty(strfind(data_cell_string{i},'DX='))
                        obj.handles.DatasetVariables.PixelResolution =  str2double(data_cell_string{i}(4:end));
                    end
                    if ~isempty(strfind(data_cell_string{i},'DZ='))
                        obj.handles.DatasetVariables.SlideSeperation =  str2double(data_cell_string{i}(4:end));
                    end
                    if ~isempty(strfind(data_cell_string{i},'TX='))
                        obj.handles.DatasetVariables.XTiles =  str2double(data_cell_string{i}(4:end));
                    end
                    if ~isempty(strfind(data_cell_string{i},'TY='))
                        obj.handles.DatasetVariables.YTiles =  str2double(data_cell_string{i}(4:end));
                    end
                end
                disp('Grab Info Txt Variables.')
                %disp(obj.handles.DatasetVariables)
                obj.handles.DatasetVariables.GridSpacingUM= 25; % default mouse settings
                obj.handles.DatasetVariables.CustomZoom=100;
                saveDatasetVariables(obj);
            end
            
        end
        
        %% saveDatasetVariables - save metadata to mat file
        function obj = saveDatasetVariables(obj)
            GlobalVariables=obj.handles.DatasetVariables;
            save(fullfile(obj.handles.lastDir,'info.mat'), 'GlobalVariables','-v7.3');
        end
        
        %% saveSlideDataStruct
        function obj = saveSlideDataStruct(obj)
            if isdir(obj.handles.Data.Directory)
                set(obj.handles.statusText,'string',['Saving data.mat to ' obj.handles.Data.Directory])
                
                SlideData=obj.handles.Data;count=1;empty_gloms=[];
                if ~isempty(SlideData.Glom)
                    for ii=length(SlideData.Glom)
                        if isempty(SlideData.Glom(ii).Label)
                            if count==1
                                empty_gloms(1)=ii;
                            else
                                empty_gloms(count)=ii;
                            end
                            count=count+1;
                        end
                    end
                end
                if ~isempty(empty_gloms)
                    SlideData.Glom = SlideData.Glom(setdiff(1:length(SlideData.Glom),empty_gloms));
                end
                save(fullfile(obj.handles.Data.Directory,'data.mat'), 'SlideData','-v7.3');
            else
                disp(['saveSlideDataStruct: no directory ' obj.handles.Data.Directory]);
            end
            
        end
        
        %% loadSlideData
        %   pull Data struct from data.mat
        %
        function  obj =loadSlideData(obj, curdirectory, prevslidedirectory)
            if nargin < 2
                curdirectory= fullfile(obj.handles.lastDir,obj.handles.CurrentSlideDir);
            end
            if nargin <3
                prevslidedirectory='';
            end
            
            if isempty(curdirectory)
                return
            end
            if exist(fullfile(curdirectory,'data.mat'), 'file')
                load(fullfile(curdirectory,'data.mat'),'SlideData');
                obj.handles.Data = SlideData;
                
                
            elseif isdir(prevslidedirectory) && exist(fullfile(prevslidedirectory,'data.mat'), 'file')
                
                %%  Pass gloms from previous slide to current slide
                load(fullfile(prevslidedirectory,'data.mat'),'SlideData');
                for i=1:numel(SlideData.Glom)
                    SlideData.Glom(i).TuftGridPoints=[];
                    SlideData.Glom(i).CapsuleGridPoints=[];
                end
                SlideData.Info=obj.handles.iminfo;
                dir_parts = regexp(curdirectory,'/','split');
                if strcmp(dir_parts{end},'')
                    dir_parts(end)=[];
                end
                slide_dirname= regexp(dir_parts{end},'_','split');
                SlideData.SlideNumber=str2double(slide_dirname{1});
                SlideData.TileRows=str2double(slide_dirname{2});
                SlideData.TileCols=str2double(slide_dirname{3});
                obj.handles.Data = SlideData;
                saveSlideDataStruct(obj);
                %eval(obj.handles.inlineSlideSave)
                
            else
                %% default method, create new Data structure for slide
                obj=makeNewSlideDataFile(obj,curdirectory);
                saveSlideDataStruct(obj)
            end
            
            if obj.handles.Data.GridSpacingUM ~= obj.handles.DatasetVariables.GridSpacingUM
                %uiwait(msgbox('Grid spacing not consistent.  Please ensure all grid points in this slide to match the whole dataset''s  grid spacing.','GlomViewer Error','modal'))
                ButtonName = questdlg('Grid spacing not consistent. This will overwrite the current slide''s (local) grid spacing or the whole dataset''s (global) grid spacing. What is your grid spacing choice?', ...
                    'Grid Spacing Question', ...
                    ['Local ' num2str(obj.handles.Data.GridSpacingUM) ' microns'], ['Global ' num2str(obj.handles.DatasetVariables.GridSpacingUM) ' um'], 'Other');
                switch ButtonName,
                    case num2str(obj.handles.Data.GridSpacingUM),
                        obj.handles.DatasetVariables.GridSpacingUM= obj.handles.Data.GridSpacingUM;
                        disp('Your favorite color is Red');
                    case num2str(obj.handles.DatasetVariables.GridSpacingUM),
                        obj.handles.Data.GridSpacingUM=obj.handles.DatasetVariables.GridSpacingUM;
                end % switch
                saveSlideDataStruct(obj);
                saveDatasetVariables(obj);
            end
        end
        
        
        %% makeNewSlideDataFile
        %  Create a new 'data.mat' for an image
        %  to store variables relating to the image
        function obj = makeNewSlideDataFile(obj,dirname)
            clear obj.handles.Data
            dir_parts = regexp(dirname,'/','split');
            if strcmp(dir_parts{end},'')
                dir_parts(end)=[];
            end
            slide_dirname= regexp(dir_parts{end},'_','split');
            
            obj.handles.Data.DarisID = dir_parts{end-1};
            
            obj.handles.Data.Directory = dirname;
            obj.handles.Data.Filename = '0001.tif';
            obj.handles.Data.Info=obj.handles.iminfo;
            
            obj.handles.Data.GridSpacingUM = obj.handles.DatasetVariables.GridSpacingUM;
            obj.handles.Data.SlideNumber=str2double(slide_dirname{1});
            obj.handles.Data.TileRows=str2double(slide_dirname{2});
            obj.handles.Data.TileCols=str2double(slide_dirname{3});
            obj.handles.Data.NumGloms=0;
            obj.handles.Data.MaxLabel=0;
            obj.handles.Data.Glom = [];
            
        end
        
        
        
        %% resizeFcn
        %   This resizes the figure window appropriately
        %------------------------------------------------------------------
        function obj = resizeFcn(obj, varargin)
            LoadBtnCallback(obj);
            set(obj.handles.GlomViewer, 'units', 'pixels');
            figPos = get(obj.handles.GlomViewer, 'position');
            
            % figure can't be too small or off the screen
            if figPos(3) < 750 || figPos(4) < 500
                figPos(3) = max([750 figPos(3)]);
                figPos(4) = max([500 figPos(4)]);
                screenSize = get(0, 'screensize');
                if figPos(1)+figPos(3) > screenSize(3)
                    figPos(1) = screenSize(3) - figPos(3) - 50;
                end
                if figPos(2)+figPos(4) > screenSize(4)
                    figPos(2) = screenSize(4) - figPos(4) - 50;
                end
                
                set(obj.handles.GlomViewer, 'position', figPos);
                
            end
            disp(figPos)
            %% Constants
            bottomImagePos= 40; topPanelHeight=80;
            previewHeight=220;leftPanelWidth=260;
            
            ImageHeight=figPos(4)-bottomImagePos-topPanelHeight;
            ImageWidth = figPos(3)-leftPanelWidth-10;
            PanelHeight=floor(ImageHeight- previewHeight-20);
            dataHeight=ceil(PanelHeight*2/3);filelistHeight=floor(PanelHeight/3);
            
            
            set(obj.handles.versionPanel         , 'position', [1, 1, 100, 25]);
            set(obj.handles.versionText          , 'position', [2, 2, 96, 20]);
            set(obj.handles.statusPanel          , 'position', [102, 1, figPos(3)-102, 25]);
            set(obj.handles.statusText           , 'position', [2, 2, figPos(3)-107, 20]);
            %--------------frame1
            set(obj.handles.frame1               , 'position', [figPos(3)-115, figPos(4)-55, 110, 53]);
            set(obj.handles.ZoomCaptionText      , 'position', [5, 22, 100, 17]);
            set(obj.handles.ResetViewBtn1        , 'position', [5, 2, 47, 20]);
            set(obj.handles.ResetViewBtn2        , 'position', [55, 2, 47, 20]);
            %--------------frame1
            
            set(obj.handles.HelpBtn              , 'position', [figPos(3)-220, figPos(4)-28, 100, 20]);
            set(obj.handles.FileInfoBtn          , 'position', [figPos(3)-220, figPos(4)-50, 100, 20]);
            
            %--------------framegrid
            set(obj.handles.framegrid            , 'position', [figPos(3)-440, figPos(4)-55, 210, 53]);
            set(obj.handles.GridBtn              , 'position', [105, 2, 100, 40]);
            set(obj.handles.GridTextBox         , 'position', [5, 2, 60, 14]);
            set(obj.handles.GridValueBox         , 'position', [65, 2, 35, 14]);
            set(obj.handles.GridSpacingBox       , 'position', [5, 22, 100, 18]);
            %           set(obj.handles.GridBtn              , 'position', [figPos(3)-330, figPos(4)-28, 100, 40]);
            %           set(obj.handles.GridValueBox         , 'position', [figPos(3)-440, figPos(4)-50, 100, 20]);
            %           set(obj.handles.GridSpacingBox       , 'position', [figPos(3)-440, figPos(4)-28, 100, 20]);
            
            %--------------framegrid
            
            set(obj.handles.RefreshBtn           , 'position', [figPos(3)-550, figPos(4)-50, 100, 40]);
            set(obj.handles.SaveCSVBtn              , 'position', [leftPanelWidth+20, figPos(4)-50, 100, 40]);
            
            
            if obj.handles.debug
                
                set(obj.handles.SaveBtn              , 'position', [leftPanelWidth+20, figPos(4)-28, 100, 20]);
                set(obj.handles.SaveCSVBtn              , 'position', [leftPanelWidth+20, figPos(4)-50, 100, 20]);
                set(obj.handles.ShowDataStructBtn    , 'position', [leftPanelWidth+20+110, figPos(4)-28, 100, 20]);
                set(obj.handles.ShowGlomDataBtn      , 'position', [leftPanelWidth+20+110, figPos(4)-50, 100, 20]);
                set(obj.handles.DeleteGridPointsBtn  , 'position', [leftPanelWidth+20+220, figPos(4)-28, 100, 20]);
                set(obj.handles.DeleteCurrentGlomBtn   , 'position', [leftPanelWidth+20+220, figPos(4)-50, 100, 20]);
                set(obj.handles.DeleteAllGridPointsBtn , 'position', [leftPanelWidth+20+330, figPos(4)-28, 100, 20]);
                set(obj.handles.LoadBtn              , 'position', [leftPanelWidth+20+330, figPos(4)-50, 100, 20]);
                set(obj.handles.UserDirectoryBtn  , 'position', [237, figPos(4)-30, 20, 20], 'visible','on','enable','on');
             set(obj.handles.UserDirectoryPanel, 'position', [20, figPos(4)-30, 215, 20]);
            set(obj.handles.UserDirectoryEdit , 'position', [1, 1, 213, 18]);               
            else
                
                set(obj.handles.SaveBtn              , 'position', [leftPanelWidth+20, figPos(4)-28, 100, 20], 'visible','off');
                set(obj.handles.SaveCSVBtn           , 'position', [leftPanelWidth+20, figPos(4)-50, 100, 40], 'visible','on');
                set(obj.handles.ShowDataStructBtn    , 'position', [leftPanelWidth+20+110, figPos(4)-28, 100, 20], 'visible','off');
                set(obj.handles.ShowGlomDataBtn      , 'position', [leftPanelWidth+20+110, figPos(4)-50, 100, 20], 'visible','off');
                set(obj.handles.DeleteGridPointsBtn  , 'position', [leftPanelWidth+20+220, figPos(4)-28, 100, 20], 'visible','off');
                set(obj.handles.DeleteCurrentGlomBtn   , 'position', [leftPanelWidth+20+220, figPos(4)-50, 100, 20], 'visible','off');
                set(obj.handles.DeleteAllGridPointsBtn , 'position', [leftPanelWidth+20+330, figPos(4)-28, 100, 20], 'visible','off');
                set(obj.handles.LoadBtn              , 'position', [leftPanelWidth+20+330, figPos(4)-50, 100, 20], 'visible','off');
                set(obj.handles.UserDirectoryBtn  , 'position', [237, figPos(4)-30, 20, 20], 'visible','off');
            set(obj.handles.UserDirectoryPanel, 'position', [20, figPos(4)-30, 215, 20], 'visible','off');
            set(obj.handles.UserDirectoryEdit , 'position', [1, 1, 213, 18], 'visible','off');
                 
            end

            set(obj.handles.CurrentDirectoryPanel, 'position', [20, figPos(4)-50, 215, 20]);
            set(obj.handles.CurrentDirectoryEdit , 'position', [1, 1, 213, 18]);
            set(obj.handles.ChooseDirectoryBtn   , 'position', [237, figPos(4)-50, 20, 20]);
            set(obj.handles.UpDirectoryBtn       , 'position', [259, figPos(4)-50, 20, 20]);
            set(obj.handles.FileListBox          , 'position', [10, bottomImagePos+previewHeight+dataHeight+20, leftPanelWidth, filelistHeight]);
            set(obj.handles.DataTreeBox          , 'position', [10, bottomImagePos+previewHeight+10, leftPanelWidth, dataHeight]);

            
            set(obj.handles.PreviewImagePanel    , 'position', [10, bottomImagePos, leftPanelWidth, previewHeight]);
            set(obj.handles.PreviewImageAxes     , 'position', [5, 5, leftPanelWidth-10, previewHeight-20]);
            
            set(obj.handles.ImageAxes            , 'position', [leftPanelWidth+20, ...
                bottomImagePos, figPos(3)-310, ImageHeight]);
            
            %            set(obj.handles.ImageAxes,'LegendColorbarListeners',[]);
            %setappdata(obj.handles.ImageAxes,'LegendColorbarManualSpace',1);
            %setappdata(obj.handles.ImageAxes,'LegendColorbarReclaimSpace',1);
            axPos = get(obj.handles.ImageAxes    , 'position');
            
            textBoxDim  = [400, 400];
            rightMargin = figPos(3)-(axPos(1)+axPos(3));
            topMargin   = figPos(4)-(axPos(2)+axPos(4));
            set(obj.handles.MessageTextBox, 'position', [figPos(3)-rightMargin-textBoxDim(1), ...
                figPos(4)-topMargin-textBoxDim(2), ...
                textBoxDim]);
            
            set(obj.handles.DataTreeContentBox, 'position', ...
                [figPos(3)-rightMargin-textBoxDim(1), ...
                figPos(4)-topMargin-textBoxDim(2), ...
                textBoxDim]);
            
            %         [leftPanelWidth+20, ...
            %                 figPos(4)-topMargin-textBoxDim(2), ...
            %                 textBoxDim]);
            %bottomImagePos+previewHeight+10, leftPanelWidth, dataHeight]);
            
            obj.handles.figPos = figPos;
            obj.handles.axPos  = axPos;
            
            titleStr = get(get(obj.handles.ImageAxes, 'title'), 'string');
            if ~isempty(titleStr)
                % resize image as well
                obj=loadImage(obj, [], titleStr);
                RefreshBtnCallback(obj);
            end
            
            
        end
        
        
        %% fileInfoBtnCallback
        %   This displays the file info of the image that's displayed
        %------------------------------------------------------------------
        function fileInfoBtnCallback(obj, varargin)
            
            hObj = varargin{1};
            %get(obj.handles.MessageTextBox,'position')
            if get(hObj, 'value')
                
                fnames      = fieldnames(obj.handles.iminfo); %fnames = fnames(info_elements)
                
                vals        = struct2cell(obj.handles.iminfo); %vals = vals(info_elements);
                arrayID=zeros(1,length(vals));
                numdID      = cellfun(@numel,vals);
                empty = cellfun(@isempty,vals);
                dID         = cellfun('isclass', vals, 'double') .* (~empty);
                arrayID(  logical(numdID>1) & logical(dID==1))=1;
                
                dID( find(arrayID))=0;
                
                
                %dID=logical(dID .* (~arrayID)');
                exclude_imDesc= cellfun(@isempty,strfind(fnames,'ImageDescription'));
                dID=logical(dID);
                
                % Only show file name (not full path)
                [p, n, e]   = fileparts(vals{1}); %#ok<ASGLU>
                vals{1}     = [n e];
                sID         = logical(cellfun('isclass', vals, 'char') .* exclude_imDesc);
                info_elements = sort([find(sID)' find(dID)']);
                
                fmt         = cell(2, numel(info_elements));
                fmt(1, :)   = repmat({'%s  : '}, 1, numel(info_elements));
                fmt(2, logical(sID(info_elements))) = repmat({'%s|'}, 1, length(find(sID(info_elements))));
                fmt(2, logical(dID(info_elements))) = repmat({'%d|'}, 1, length(find(dID(info_elements))));%repmat( horzcat(repmat('%g|',1,numdID(find(dID)))), 1, length(find(dID)));
                %fmt(2, logical(empty)) = repmat({'  |'}, 1, length(find(empty)));
                tmp         = [fnames(info_elements),vals(info_elements)]';
                str         = sprintf([fmt{:}], tmp{:});
                
                % remove non-utf8 characters
                str(regexp(str,'[^\x00-\x7F]'))=' ';
                
                imdesc = find(~exclude_imDesc);
                if ~isempty(imdesc)
                    str =  [ str char(fnames(imdesc)) ': ' char(vals(imdesc))];
                end
                size(str)
                set(obj.handles.MessageTextBox, 'string', str, 'visible', 'on');
                
            else
                set(obj.handles.MessageTextBox, 'visible', 'off');
                
            end
            
        end
        
        %% SaveBtn
        function SaveBtnCallback(obj,varargin)
            saveSlideDataStruct(obj);
        end

        
        %% SaveCSVBtnCallback
        % Export mat files to CSV files
        %
        function SaveCSVBtnCallback(obj,varargin)
            LoadBtnCallback(obj);
            [filename, pathname] = uiputfile('*.csv', 'Make a base CSV name for ''-glom'' and ''-summary'' files.');
            if isequal(filename,0) || isequal(pathname,0)
                disp('User pressed cancel');
            else
                disp(['User selected ', fullfile(pathname, filename)]);
            end
            glomfilename = fullfile(pathname, regexprep(filename,'.csv','-gloms.csv'));
            summfilename = fullfile(pathname, regexprep(filename,'.csv','-summary.csv'));
            
            
            %% Save the current slide data
            saveSlideDataStruct(obj);
            
            %% Get slide dirnames
            d = dir(obj.handles.lastDir);
            
            dirnames = sort({d([d.isdir]).name});
            dirnames(ismember(dirnames, {'.', '..'})) = '';
            SlideDataNames = strcat(dirnames,repmat({'/data.mat'},1,length(dirnames)));
            
            TotalGloms=0;
            for ii=1:length(dirnames)
                if exist(fullfile(obj.handles.lastDir,char(dirnames(ii)),'data.mat'),'file')
                    load(fullfile(obj.handles.lastDir,char(dirnames(ii)),'data.mat'),'SlideData');
                    TotalGloms=TotalGloms+SlideData.NumGloms;
                end
            end
            disp(['Quick check of total gloms in all slides: ' num2str(TotalGloms)])
            Summary= zeros(6,TotalGloms);
            
          
            %            hProgress  = progressbar([],0,'Please wait, writing GlomViewer data to file.' );
            
            %ShowStatus('Saving statistics of labelled image.',handles);
            dlmwrite(summfilename, 'GlomViewer Export Glomeruli Grid Data ', 'delimiter', '' );
            dlmwrite(summfilename, ['Date :,', datestr(now) ], 'delimiter', '' ,'-append');
            dlmwrite(summfilename, ['Datset :,', obj.handles.Data.DarisID ], 'delimiter', '' ,'-append');
            dlmwrite(summfilename, ['PixelResolution : ,', num2str(obj.handles.DatasetVariables.PixelResolution)], 'delimiter', '' ,'-append');
            dlmwrite(summfilename, ['SlideSeperation : ,', num2str(obj.handles.DatasetVariables.SlideSeperation)], 'delimiter', '' ,'-append');
            dlmwrite(summfilename, ['GridSpacingUM : ,', num2str(obj.handles.DatasetVariables.GridSpacingUM)], 'delimiter', '','-append' );
            dlmwrite(summfilename, ['NumSlides : ,' num2str(obj.handles.DatasetVariables.NumSlides)], 'delimiter', '' ,'-append');
            dlmwrite(summfilename, ['ImageTile: ,' num2str(obj.handles.DatasetVariables.XTiles) ',' num2str(obj.handles.DatasetVariables.YTiles)], 'delimiter', '' ,'-append');
            
            dlmwrite(summfilename,...
                'SlideNum, ID, Xpos, YPos,  TuftPoints, CapsulePoints','delimiter','','-append');
            count=1;
            dlmwrite(glomfilename, 'GlomViewer Export Glomeruli Grid Data ', 'delimiter', '' );
            dlmwrite(glomfilename, ['Date :,', datestr(now) ], 'delimiter', '' ,'-append');
            dlmwrite(glomfilename, ['Datset :,', obj.handles.Data.DarisID ], 'delimiter', '' ,'-append');
            dlmwrite(glomfilename, ['PixelResolution : ,', num2str(obj.handles.DatasetVariables.PixelResolution)], 'delimiter', '' ,'-append');
            dlmwrite(glomfilename, ['SlideSeperation : ,', num2str(obj.handles.DatasetVariables.SlideSeperation)], 'delimiter', '' ,'-append');
            dlmwrite(glomfilename, ['GridSpacingUM : ,', num2str(obj.handles.DatasetVariables.GridSpacingUM)], 'delimiter', '' ,'-append');
            dlmwrite(glomfilename, ['NumSlides : ,' num2str(obj.handles.DatasetVariables.NumSlides)], 'delimiter', '' ,'-append');
            dlmwrite(glomfilename, ['ImageTile: ,' num2str(obj.handles.DatasetVariables.XTiles) ',' num2str(obj.handles.DatasetVariables.YTiles)], 'delimiter', '' ,'-append');
            %% Write data to CSV
            for ii=1:length(dirnames)
                if exist(fullfile(obj.handles.lastDir,char(dirnames(ii)),'data.mat'),'file')
                    load(fullfile(obj.handles.lastDir,char(dirnames(ii)),'data.mat'),'SlideData');
                    dlmwrite(glomfilename, '***************************', 'delimiter', ',' ,'-append');
                    dlmwrite(glomfilename, [ 'Slide Number', num2str(SlideData.SlideNumber) ], 'delimiter', '' ,'-append');
                    dlmwrite(glomfilename, '---------------------------', 'delimiter', ',' ,'-append');
                    
                    for jj=1:SlideData.NumGloms
                        if ~isempty(SlideData.Glom(jj).Label) && ~isempty(SlideData.Glom(jj).Location)
                            dlmwrite(glomfilename, ['Label,', SlideData.Glom(jj).Label], 'delimiter', '' ,'-append');
                            dlmwrite(glomfilename, ['Location,', num2str(SlideData.Glom(jj).Location(1)) ' , ' num2str(SlideData.Glom(jj).Location(2))], 'delimiter', '' ,'-append');
                            dlmwrite(glomfilename, ['TuftGridPoints ,', num2str(size(SlideData.Glom(jj).TuftGridPoints,1))], 'delimiter', '' ,'-append');
                            dlmwrite(glomfilename,  SlideData.Glom(jj).TuftGridPoints, 'delimiter', ',' ,'-append');
                            dlmwrite(glomfilename, ['CapsuleGridPoints ,', num2str(size(SlideData.Glom(jj).CapsuleGridPoints,1))]  , 'delimiter', '' ,'-append');
                            dlmwrite(glomfilename,  SlideData.Glom(jj).CapsuleGridPoints, 'delimiter', ',' ,'-append');
                            dlmwrite(glomfilename, '---------------------------', 'delimiter', ',' ,'-append');
                            Summary(:,count) = [SlideData.SlideNumber str2double(SlideData.Glom(jj).Label(2:end)) SlideData.Glom(jj).Location(1) SlideData.Glom(jj).Location(2) size(SlideData.Glom(jj).TuftGridPoints,1) size(SlideData.Glom(jj).CapsuleGridPoints,1)];
                            count=count+1;
                        end
                    end
                    %                hProgress  = progressbar(hProgress,double(1.0/length(dirnames)),'Please wait, writing standard labels file.' );
                end
            end
            
            
            dlmwrite(summfilename,Summary','-append');
            %            hProgress  = progressbar(hProgress,0.5,'Please wait, writing standard labels file.' );
            
            %             progressbar( hProgress, -1 );
            %             if ~hProgress
            %                 disp('Progressbar removed.')
            %             end
            
            
            %ShowStatus('Statistics of labelled image file written.',handles);
            
        end
        
        function obj = LoadBtnCallback(obj,varargin)
            if exist(fullfile(obj.handles.Data.Directory ,'data.mat'),'file')
                load(fullfile(obj.handles.Data.Directory ,'data.mat'),'SlideData');
                %disp(Data.Glom)
                obj.handles.Data = SlideData;
            end
            
        end
        
        %         function StateChangeCallback(obj,varargin)
        %
        %         end
        
        %% DeleteCurrentGlomGridPoints
        %
        function obj = DeleteCurrentGlomGridPoints(obj,varargin)
            xl = get(obj.handles.ImageAxes, 'xlim'); midX = mean(xl);
            yl = get(obj.handles.ImageAxes, 'ylim'); midY = mean(yl);
            
            if ~obj.handles.Data.NumGloms
                cur = GetCurrentGlom(obj,midX,midY);
                obj.handles.Data.Glom(cur).TuftGridPoints=[];
                obj.handles.Data.Glom(cur).CapsuleGridPoints=[];
                saveSlideDataStruct(obj);
            end
            LoadBtnCallback(obj);
            
        end
        
        % DeleteAllCurrentGlomGridPoints
        %
        %
        %
        function obj = DeleteAllCurrentSlideGridPoints(obj,varargin)
            
            if ~obj.handles.Data.NumGloms
                for i = 1:obj.handles.Data.NumGloms
                    obj.handles.Data.Glom(i).TuftGridPoints=[];
                    obj.handles.Data.Glom(i).CapsuleGridPoints=[];
                end
                saveSlideDataStruct(obj);
            end
            LoadBtnCallback(obj)
        end
        
        %
        %
        %
        function obj = DeleteCurrentGlom(obj,varargin)
            xl = get(obj.handles.ImageAxes, 'xlim'); midX = mean(xl);
            yl = get(obj.handles.ImageAxes, 'ylim'); midY = mean(yl);
            
            if ~obj.handles.Data.NumGloms
                cur = GetCurrentGlom(obj,midX,midY);
                obj.handles.Data.Glom(cur)=[];
                obj.handles.Data.NumGloms= obj.handles.Data.NumGloms-1;
                
            end
            saveSlideDataStruct(obj);
        end
        
        function obj = GridValueBoxCallback(obj,varargin)
            msgbox('Grid spacing can only be changed in custom mode.  Please select custom grid spacing in the option list above.','GlomViewer Error','modal');
            set(obj.handles.GridValueBox,'string',num2str(obj.handles.Data.GridSpacingUM));
        end
        
        %
        %
        %
        function obj = GridSpacingBoxCallback(obj,varargin)
            contents = cellstr(get(obj.handles.GridSpacingBox,'String'));
            disp(contents);
            selection=get(obj.handles.GridSpacingBox,'Value');
            Spacing=35;
            
            switch selection
                case 1
                    Spacing=25;
                case 2
                    Spacing=35;
                case 3
                    Spacing=45;
                case 4
                    answer=inputdlg('Enter the custom grid spacing in microns. (Default is 25 microns)','GlomViewer Custom Grid Spacing',1,{'25'});
                    if ~isempty(answer{1})
                        val=str2double(answer{1});  % Use curly bracket for subscript
                    else
                        val=25;
                    end
                    Spacing=val;
                    % val is a scalar or matrix converted from the first input
            end
            obj.handles.DatasetVariables.GridSpacingUM=Spacing;
            obj.handles.Data.GridSpacingUM=Spacing;
            set(obj.handles.GridValueBox,'string', num2str(Spacing));
            saveDatasetVariables(obj);
            saveSlideDataStruct(obj);
        end
        
        
        %% showDirectory
        %   This function shows a list of image files in the directory
        %--------------------------------------------------------------------------
        function obj = showDirectory(obj, dirname)
            
            % Reset settings and images
            stopTimer(obj);
            cla(obj.handles.PreviewImageAxes);
            axis(obj.handles.PreviewImageAxes, 'normal');
            clearImageAxes(obj);
            %----------------------------------------------------------------------
            
            if nargin == 2
                obj.handles.lastDir = dirname;
            else
                if isempty(obj.handles.lastDir)
                    obj.handles.lastDir = pwd;
                end
            end
            
            set(obj.handles.CurrentDirectoryEdit, 'string', ...
                fixLongDirName(obj.handles.lastDir), ...
                'tooltipstring', obj.handles.lastDir);
            
            % Get image formats
            imf  = imformats;
            exts = lower([imf.ext]);
            exts_str = sprintf('%s|', exts{:}); exts_str(end) = '';
            d = dir(obj.handles.lastDir);
            
            filenames = sort({d(~[d.isdir]).name});
            filenames_L = lower(filenames);
            
            % Find all the image files
            imagefiles_idx = regexp(filenames_L, sprintf('[^ \f\n\r\t\v.]+\\.(%s)',exts_str));
            imagefiles = filenames(~cellfun(@isempty, imagefiles_idx));
            
            dirnames = sort({d([d.isdir]).name});
            dirnames(ismember(dirnames, {'.', '..'})) = '';
            
            
            if isempty(imagefiles)
                obj.handles.imageID = [];
                obj.handles.imageNames = {};
                obj.handles.imagePreviews = {};
                runTimer = false;
            else
                obj.handles.imageID = 1:length(imagefiles);
                obj.handles.imageNames = imagefiles;
                obj.handles.imagePreviews = cell(1,length(imagefiles));
                runTimer = true;
            end
            
            if obj.handles.DarisDirMode == 1
                obj.handles.imageID = 1:length(dirnames);
                obj.handles.imageNames = strcat(dirnames,repmat({'/overview_1024.tif'},1,length(dirnames)));
                obj.handles.imagePreviews = cell(1,length(dirnames));
                runTimer = true;
            end
            
            if isempty(dirnames)
                n = imagefiles;
            else
                dirnames = strcat(repmat({'['}, 1, length(dirnames)), dirnames, repmat({']'}, 1, length(dirnames)));
                n = [dirnames, imagefiles];
                if obj.handles.DarisDirMode == 0
                    obj.handles.imageID = obj.handles.imageID + length(dirnames);
                end
            end
            set(obj.handles.DataTreeBox,'Value',[]);
            set(obj.handles.FileListBox, 'string', n, 'value', 1);
            
            if runTimer
                startTimer(obj);
            end
            
            if ~isempty(obj.handles.imageID)
                set(obj.handles.GlomViewer, 'selectiontype', 'normal');
                set(obj.handles.FileListBox, 'value', obj.handles.imageID(1));
                fileListBoxCallback(obj);
            end
        end
        
        %--------------------------------------------------------------------------
        % getPreviewImages
        %   This is the TimerFcn for the preview timer object
        %--------------------------------------------------------------------------
        function getPreviewImages(obj, varargin)
            
            try
                
                id = find(cellfun('isempty', obj.handles.imagePreviews));
                
                if ~isempty(id)
                    set(obj.handles.statusText, 'string', ...
                        sprintf('Generating Thumbnails ... %d of %d', ...
                        length(obj.handles.imagePreviews)-length(id)+1, ...
                        length(obj.handles.imagePreviews)));
                    drawnow;
                    obj.handles.imagePreviews{id(1)} = ...
                        getPreviewImageData(obj, ...
                        fullfile(get(obj.handles.CurrentDirectoryEdit, 'tooltipstring'), ...
                        obj.handles.imageNames{id(1)}));
                    
                else % All previews are generated. Stop timer
                    set(obj.handles.statusText, 'string', '');
                    stopTimer(obj);
                    
                end
                
            catch %#ok<CTCH>
                
            end
        end
        
        %--------------------------------------------------------------------------
        % getPreviewImageData
        %   This reads in image file for thumbnails.  This file
        %   must be in the slide directory and named overview_1024.tif
        %--------------------------------------------------------------------------
        function x = getPreviewImageData(obj, filename)
            
            if ~isempty(strfind(filename,'overview_1024.tif'))
                x = readImageFileFcn(obj, filename);x = x(:,:,1:3);
            else
                x = readImageFileFcn(obj, filename);
                if ~isnan(x)
                    sz = size(x);
                    r = [200, 260] ./ sz(1:2);
                    
                    % Crude IMRESIZE (non-toolbox)
                    xd = round(linspace(1,sz(1), round(sz(1) * min(r))));
                    yd = round(linspace(1,sz(2), round(sz(2) * min(r))));
                    x = x(xd, yd, :);
                end
            end
        end
        
        %--------------------------------------------------------------------------
        % fileListBoxCallback
        %   This gets called when an entry is selected in the file list box
        %--------------------------------------------------------------------------
        function fileListBoxCallback(obj, hObj, varargin)
            
            if nargin == 1
                hObj = obj.handles.FileListBox;
            end
            stopTimer(obj);
            val = get(hObj, 'value');
            str = cellstr(get(hObj, 'string'));
            
            if ~isempty(str)
                switch get(obj.handles.GlomViewer, 'selectiontype')
                    case 'normal'   % single click - show preview
                        if (str{val}(1) == '[' && str{val}(end) == ']') && (obj.handles.DarisDirMode == 0)
                            cla(obj.handles.PreviewImageAxes);
                            axis(obj.handles.PreviewImageAxes, 'normal');
                        else
                            id = find(obj.handles.imageID == val);
                            if isempty(obj.handles.imagePreviews{id});
                                
                                %% Call getPreviewImageData
                                obj.handles.imagePreviews{id} = ...
                                    getPreviewImageData(obj, ...
                                    fullfile(...
                                    get(obj.handles.CurrentDirectoryEdit, 'tooltipstring'), ...
                                    str{val}));
                            end
                            %% Try again for first image
                            
                            if (obj.handles.DarisDirMode == 1) && ( id == 1 )
                                obj.handles.imagePreviews{1}= readImageFileFcn(obj, ...
                                    fullfile(...
                                    get(obj.handles.CurrentDirectoryEdit, 'tooltipstring'), ...
                                    str{1}(2:end-1),'overview_1024.tif'));
                            end
                            
                            if ~isnan(obj.handles.imagePreviews{id})
                                image(obj.handles.imagePreviews{id}, ...
                                    'parent', obj.handles.PreviewImageAxes, ...
                                    'hittest', 'off');
                                set(obj.handles.PreviewImagePanel, 'buttondownfcn', @previewImageClickFcn);
                                
                            else % unable to load image
                                cla(obj.handles.PreviewImageAxes);
                                set(obj.handles.PreviewImagePanel, 'buttondownfcn', '');
                                text(0.5, 0.5, 'Can''t Load Image', ...
                                    'parent', obj.handles.PreviewImageAxes, ...
                                    'horizontalalignment', 'center', ...
                                    'verticalalignment', 'middle');
                                set(obj.handles.PreviewImageAxes, 'xlim', [0 1], 'ylim', [0 1]);
                                
                            end
                            
                            axis(obj.handles.PreviewImageAxes, 'equal');
                            set(obj.handles.PreviewImageAxes, 'visible', 'off');
                            
                        end
                        startTimer(obj);
                        
                    case 'open'   % double click - open image and display
                        
                        if str{val}(1) == '[' && str{val}(end) == ']'
                            if obj.handles.DarisDirMode
                                disp(['Using daris based folder. Opening 0001.tif inside dir ' str{val}(2:end-1)])
                                %if ~isempty(obj.handles.ImX{val}) % = [];
                                obj.handles.ImX = [];
                                    obj.handles.CurrentSlideDir=str{val}(2:end-1);
                                    fullfile(get(obj.handles.CurrentDirectoryEdit, 'tooltipstring'),str{val}(2:end-1) ,'overview_1024.tif')
                                    obj=loadImage(obj, val, fullfile(get(obj.handles.CurrentDirectoryEdit, 'tooltipstring'),str{val}(2:end-1) ,'0001.tif'));
                                % end
                                % if val > 1
                                %     loadSlideData(obj,fullfile(get(obj.handles.CurrentDirectoryEdit, 'tooltipstring'),str{val}(2:end-1)), fullfile(get(obj.handles.CurrentDirectoryEdit, 'tooltipstring'),str{val-1}(2:end-1)));
                                % else
                                loadSlideData(obj,fullfile(get(obj.handles.CurrentDirectoryEdit, 'tooltipstring'),str{val}(2:end-1)))
                                % end
                                set(obj.handles.DataTreeBox,'Value',1);
                                obj.handles.GridMode=0;
                                obj.handles.CurrentGlom=0;
                                RefreshLabelEllipses(obj);
                                startTimer(obj);
                            else
                                dirname = get(obj.handles.CurrentDirectoryEdit, 'tooltipstring');
                                newdirname = fullfile(dirname, str{val}(2:end-1));
                                if exist(fullfile(newdirname,'info.txt'),'file')
                                    obj.handles.DarisDirMode=1;
                                    obj = loadGlobalVariables(obj,newdirname)
                                end
                                
                                obj = showDirectory(obj, newdirname)
                            end
                        else
                            obj.handles.ImX = [];
                            obj=loadImage(obj,[], fullfile(get(obj.handles.CurrentDirectoryEdit, 'tooltipstring'), str{val}));
                            
                            startTimer(obj);
                        end
                end
                
            end
            
            %--------------------------------------------------------------------------
            % DataTreeBoxCallback
            %   This gets called when an entry is selected in the file list box
            %--------------------------------------------------------------------------
            function DataTreeBoxCallback(obj, hObj, varargin)
                
                if nargin == 1
                    hObj = obj.handles.DataTreeBox;
                end
                stopTimer(obj);
                val = get(hObj, 'value');
                str = cellstr(get(hObj, 'string'));
                
                if ~isempty(str)
                    
                    switch get(obj.handles.GlomViewer, 'selectiontype')
                        case 'normal'   % single click - do nothing
                            
                            %                         if str{val}(1) == '[' && str{val}(end) == ']'
                            %                             cla(obj.handles.PreviewImageAxes);
                            %                             axis(obj.handles.PreviewImageAxes, 'normal');
                            %
                            %                         else
                            %                             id = find(obj.handles.imageID == val);
                            %                             if isempty(obj.handles.imagePreviews{id});
                            %                                 obj.handles.imagePreviews{id} = ...
                            %                                     getPreviewImageData(obj, ...
                            %                                     fullfile(...
                            %                                     get(obj.handles.CurrentDirectoryEdit, 'tooltipstring'), ...
                            %                                     str{val}));
                            %                             end
                            %
                            %                             if ~isnan(obj.handles.imagePreviews{id})
                            %                                 image(obj.handles.imagePreviews{id}, ...
                            %                                     'parent', obj.handles.PreviewImageAxes, ...
                            %                                     'hittest', 'off');
                            %                                 set(obj.handles.PreviewImagePanel, 'buttondownfcn', @previewImageClickFcn);
                            %
                            %                             else % unable to load image
                            %                                 cla(obj.handles.PreviewImageAxes);
                            %                                 set(obj.handles.PreviewImagePanel, 'buttondownfcn', '');
                            %                                 text(0.5, 0.5, 'Can''t Load Image', ...
                            %                                     'parent', obj.handles.PreviewImageAxes, ...
                            %                                     'horizontalalignment', 'center', ...
                            %                                     'verticalalignment', 'middle');
                            %                                 set(obj.handles.PreviewImageAxes, 'xlim', [0 1], 'ylim', [0 1]);
                            %
                            %                             end
                            %
                            %                             axis(obj.handles.PreviewImageAxes, 'equal');
                            %                             set(obj.handles.PreviewImageAxes, 'visible', 'off');
                            %
                            %                         end
                            %
                            
                        case 'open'   % double click - if its a glom jump to glom
                            
                            
                            %                         if str{val}(1) == '[' && str{val}(end) == ']'
                            %                             dirname = get(obj.handles.CurrentDirectoryEdit, 'tooltipstring');
                            %                             newdirname = fullfile(dirname, str{val}(2:end-1));
                            %                             showDirectory(obj, newdirname)
                            %
                            %                         else
                            %                             obj.handles.ImX = [];
                            %                             loadImage(obj, fullfile(get(obj.handles.CurrentDirectoryEdit, 'tooltipstring'), str{val}));
                            %
                            %                             startTimer(obj);
                            %                         end
                    end
                    
                end
                startTimer(obj);
            end
            
            
            
            
            %----------------------------------------------------------------------
            % previewImageClickFcn
            %   This loads the image when the thumbnail is double-clicked
            %----------------------------------------------------------------------
            function previewImageClickFcn(varargin)
                
                switch get(obj.handles.GlomViewer, 'selectiontype')
                    
                    case 'open'   % double-click
                        
                        stopTimer(obj);
                        
                        obj.handles.ImX = [];
                        obj=loadImage(obj, [], fullfile(get(obj.handles.CurrentDirectoryEdit, 'tooltipstring'), str{val}));
                        
                        startTimer(obj);
                        
                end
            end
            
        end
        
        %--------------------------------------------------------------------------
        % chooseDirectoryCallback
        %   This opens a directory selector
        %--------------------------------------------------------------------------
        function chooseDirectoryCallback(obj, varargin)
            
            stopTimer(obj);
            dirname = uigetdir(get(obj.handles.CurrentDirectoryEdit, 'tooltipstring'), ...
                'Choose Directory');
            if ischar(dirname)
                obj =showDirectory(obj, dirname)
            end
            
        end
        
        %--------------------------------------------------------------------------
        % chooseUserDirectoryCallback
        %   This opens a directory selector
        %--------------------------------------------------------------------------
        function chooseUserDirectoryCallback(obj, varargin)
            
            stopTimer(obj);
            dirname = uigetdir(get(obj.handles.UserDirectoryEdit, 'tooltipstring'), ...
                'Choose User Directory');
            if ischar(dirname)
                obj.handles.UserDirectory = dirname;
                set(obj.handles.UserDirectoryEdit, 'string', ...
                    fixLongDirName(dirname), ...
                    'tooltipstring', dirname);
            end
            
        end
        
        %--------------------------------------------------------------------------
        % upDirectoryCallback
        %   This moves up the current directory
        %--------------------------------------------------------------------------
        function upDirectoryCallback(obj, varargin)
            
            stopTimer(obj);
            dirname = get(obj.handles.CurrentDirectoryEdit, 'tooltipstring');
            dirname2 = fileparts(dirname);
            if ~isequal(dirname, dirname2)
                obj=showDirectory(obj, dirname2)
            end
            
        end
        
        
        
        
        %--------------------------------
        % ShowGlomDataBtnCallback
        %  show data in messagebox2
        %--------------------------------
        function ShowGlomDataBtnCallback(obj,varargin)
            
            hObj = varargin{1};
            get(obj.handles.DataTreeContentBox,'position')
            if get(hObj, 'value')
                disp('ShowGlomDataBtnCallback  on ')
                fnames      = fieldnames(obj.handles.Data); %fnames = fnames(info_elements)
                
                %                 vals        = struct2cell(obj.handles.Data); %vals = vals(info_elements);
                %
                %                 % Only show file name (not full path)
                %                 [p, n, e]   = fileparts(vals{1}); %#ok<ASGLU>
                %                 vals{1}     = [n e];
                %                 sID         = cellfun('isclass', vals(info_elements), 'char');
                %                 dID         = cellfun('isclass', vals(info_elements), 'double');
                %                 fmt         = cell(2, numel(info_elements));
                %                 fmt(1, :)   = repmat({'%20s : '}, 1, numel(info_elements));
                %                 fmt(2, sID) = repmat({'%s|'}, 1, length(find(sID)));
                %                 fmt(2, dID) = repmat({'%g|'}, 1, length(find(dID)));
                %                 tmp         = [fnames(info_elements),vals(info_elements)]';
                %                 str         = sprintf([fmt{:}], tmp{:});
                %                 if numel(fnames) >= 36
                %                     str =  [ str char(fnames(36)) char(vals(36))]
                %                 end
                str= evalc('disp(obj.handles.Data)');
                regexprep(str,'\n','|')
                %set(obj.handles.DataTreeContentBox, 'string',str, 'visible', 'on');
                set(obj.handles.MessageTextBox, 'string',str, 'visible', 'on');
                
            else
                disp('ShowGlomDataBtnCallback  off ')
                %set(obj.handles.DataTreeContentBox, 'visible', 'off');
                set(obj.handles.MessageTextBox, 'visible', 'off');
                
            end
            
        end
        
        function obj = ClearGridPoints(obj,~,varargin)
            %obj = loadSlideData(obj);
            if (obj.handles.CurrentGlom > 0) && (obj.handles.CurrentGlom <= obj.handles.Data.NumGloms)
                obj.handles.Data.Glom(obj.handles.CurrentGlom).TuftGridPoints=[];
                obj.handles.Data.Glom(obj.handles.CurrentGlom).CapsuleGridPoints=[];
                
            end
            saveSlideDataStruct(obj);
        end
        
        
        %------------------------------------------------- -------------------------
        % resetView
        %   This resets the view to "Full" or "Custom" magnification
        %   The original code was updated to zoom in/out of the centre of the current
        %   view.  The tooltip was used in the ResetViewBtn2 to store the custom
        %   scale.
        %--------------------------------------------------------------------------
        function resetView(obj, varargin)
            
            hObj = varargin{1};
            stopTimer(obj);
            set(obj.handles.MessageTextBox, 'visible', 'off');
            set(obj.handles.DataTreeContentBox, 'visible', 'off');
            set(obj.handles.FileInfoBtn, 'value', false);
            set(obj.handles.ShowGlomDataBtn, 'value', false);
            xl = xlim(obj.handles.ImageAxes); midX = mean(xl);
            yl = ylim(obj.handles.ImageAxes); midY = mean(yl);
            xdiff100 = diff(obj.handles.xlim100);
            ydiff100 = diff(obj.handles.ylim100);
            custom_scale=get(obj.handles.ResetViewBtn2, 'tooltipstring');
            custom_scale_cell = regexp(custom_scale ,'Custom:[\s]{0,}(\d+)[%]{0,}','tokens');
            custom_value_str = char(custom_scale_cell{1});
            customvalue = str2double(custom_value_str) / double(100.0);
            
            switch get(hObj, 'tag')
                case 'ResetViewBtn1'  % Full
                    xlimit = obj.handles.xlimFull;
                    ylimit = obj.handles.ylimFull;
                    
                case 'ResetViewBtn2'
                    switch get(obj.handles.GlomViewer, 'selectiontype')
                        %   case 'normal'
                        %       xlimit = obj.handles.xlim100  - mean(obj.handles.xlim100) + midX ;
                        %       ylimit = obj.handles.ylim100  - mean(obj.handles.ylim100) + midY ;
                        case 'alt'
                            
                            answer = inputdlg('Enter custom zoom scale (%):','GlomViewer Custom Zoom',1,custom_scale_cell{1});
                            custom_scale_cell = regexp(answer{1} ,'(\d+)[%]{0,}','tokens');
                            customvalue = str2double(char(custom_scale_cell{1})) / double(100.0);
                            set(obj.handles.ResetViewBtn2, 'tooltipstring', ['Custom: ' char(custom_scale_cell{1}) '%'])
                            obj.handles.DatasetVariables.CustomZoom=customvalue;
                            saveDatasetVariables(obj);
                            
                    end
                    xlimit =[0 xdiff100] / customvalue - ((xdiff100/2) / customvalue) + midX ;
                    ylimit =[0 ydiff100] / customvalue - ((ydiff100/2) / customvalue) + midY ;
                    
                    
                    %case 'ZoomCaptionText'
                otherwise
                    userlimit=get(hObj, 'string');
                    % get value from string: check for % sign
                    userstr = regexp(userlimit ,'(\d+)[%]{0,}','tokens');
                    uservalue = str2double(char(userstr{1})) / double(100.0);
                    
                    xlimit =[0 xdiff100] / uservalue - ((xdiff100/2) / uservalue) + midX ;
                    ylimit =[0 ydiff100] / uservalue - ((ydiff100/2) / uservalue) + midY ;
                    
                    
            end
            
            xd = (xlimit - xl)/10;
            yd = (ylimit - yl)/10;
            
            % Restore only if needed
            if ~(isequal(xd, [0 0]) && isequal(yd, [0 0]))
                
                set(obj.handles.statusText, 'string', 'Restoring View...');
                
                % Animate with "good" speed
                for id = [1, 4, 6.5, 7.8, 8.5, 9, 9.3, 9.6, 9.8, 10]
                    
                    set(obj.handles.ImageAxes, ...
                        'xlim'                , xl + xd * id, ...
                        'ylim'                , yl + yd * id, ...
                        'cameraviewanglemode' , 'auto', ...
                        'dataaspectratiomode' , 'auto', ...
                        'plotboxaspectratio'  , obj.handles.pbar);
                    set(obj.handles.ZoomCaptionText, 'string', sprintf('%d %%', ...
                        round(diff(obj.handles.xlim100)/diff(xl + xd * id)*100)));
                    
                    pause(0.01);
                end
                
                set(obj.handles.statusText, 'string', '');
                
            end
            
            startTimer(obj);
            
        end
        
        %--------------------------------------------------------------------------
        % loadImage
        %   This loads the selected image and displays it
        %--------------------------------------------------------------------------
        function obj = loadImage(obj, val, filename)
            
            try
                
                if isnan(val) 
                    val = obj.handles.CurrentImval;
                else
                    obj.handles.CurrentImval = val;
                end    
                    
                    
                if isempty(obj.handles.ImX)
                    clearImageAxes(obj);
                    set(obj.handles.statusText, 'string', 'Loading Image...');
                    drawnow;
                    hWait = msgbox('Loading Image ...','GlomViewer','modal');
                    if length(obj.handles.CachedImX) < val
                        obj.handles.CachedImX{val}=[];
                        obj.handles.CachedImInfo{val}={};
                        [obj.handles.CachedImX{val}, obj.handles.CachedImInfo{val}] = readImageFileFcn(obj, filename);
                    elseif isempty(obj.handles.CachedImX{val}) 
                        [obj.handles.CachedImX{val}, obj.handles.CachedImInfo{val}] = readImageFileFcn(obj, filename);
                    end
                    obj.handles.ImX = obj.handles.CachedImX{val};
                    obj.handles.iminfo = obj.handles.CachedImInfo{val};
                    
                    obj.handles.iminfo
                    delete(hWait);
                end
                
                
                if ~isnan(obj.handles.ImX)
                    iH = image(obj.handles.ImX, 'parent', obj.handles.ImageAxes);
                    set(iH, 'hittest', 'off');
                    axis(obj.handles.ImageAxes, 'equal');
                    set(obj.handles.ImageAxes, ...
                        'box'             , 'on', ...
                        'xtick'           , [], ...
                        'ytick'           , [], ...
                        'buttondownfcn'   , @obj.winBtnDownFcn, ...
                        'interruptible'   , 'off', ...
                        'busyaction'      , 'queue', ...
                        'handlevisibility', 'callback');
                    set(obj.handles.ResetViewBtn1, 'enable', 'on');
                    set(obj.handles.ResetViewBtn2, 'enable', 'on');
                    set(obj.handles.FileInfoBtn  , 'enable', 'on');
                    set(get(obj.handles.ImageAxes, 'title'), ...
                        'string'      , sprintf('%s', filename), ...
                        'interpreter' , 'none');
                    obj.handles.pbar     = get(obj.handles.ImageAxes, 'plotboxaspectratio');
                    obj.handles.xlimFull = get(obj.handles.ImageAxes, 'xlim');
                    obj.handles.ylimFull = get(obj.handles.ImageAxes, 'ylim');
                    
                    % If image is small, show at 100% size
                    sz = size(obj.handles.ImX);
                    obj.handles.xlim100 = sz(2)/2 + [-1, 1] * obj.handles.axPos(3)/2;
                    obj.handles.ylim100 = sz(1)/2 + [-1, 1] * obj.handles.axPos(4)/2;
                    
                    if all(obj.handles.axPos(3:4) > sz([2 1]))
                        set(obj.handles.ImageAxes, ...
                            'xlim'                , obj.handles.xlim100, ...
                            'ylim'                , obj.handles.ylim100, ...
                            'cameraviewanglemode' , 'auto', ...
                            'dataaspectratiomode' , 'auto', ...
                            'plotboxaspectratio'  , obj.handles.pbar);
                        set(obj.handles.ZoomCaptionText, 'string', '100 %');
                        
                    else
                        set(obj.handles.ZoomCaptionText, 'string', sprintf('%d %%', ...
                            round(diff(obj.handles.xlim100)/diff(obj.handles.xlimFull)*100)));
                    end
                end
                
            catch ME
                errordlg({'Could not open image file', ME.message}, 'Error');
                clearImageAxes(obj);
                
            end
            
            set(obj.handles.statusText, 'string', '');
            saveSlideDataStruct(obj);
        end
        
        %--------------------------------------------------------------------------
        % clearImageAxes
        %   This clears the image axis
        %--------------------------------------------------------------------------
        function clearImageAxes(obj)
            
            cla(obj.handles.ImageAxes);
            axis(obj.handles.ImageAxes, 'normal');
            set(get(obj.handles.ImageAxes, 'title') , 'string'        , '');
            set(obj.handles.ImageAxes               , 'buttondownfcn' , '');
            set(obj.handles.ResetViewBtn1           , 'enable'        , 'off');
            set(obj.handles.ResetViewBtn2           , 'enable'        , 'off');
            set(obj.handles.FileInfoBtn             , 'enable'        , 'off', ...
                'value'         , false);
            set(obj.handles.ZoomCaptionText         , 'string'        , '');
            set(obj.handles.MessageTextBox          , 'visible'       , 'off');
            obj.handles.ImX = [];
            
        end
        
        
        
        % findNearestXover
        %   Use the current grid spacing and the input arguments to calculate the
        %   nearest cross-over of the grid.
        %
        function [cx cy] = findNearestXover(obj,x,y)
            cx = x; cy = y;
            xmax = ceil(x / obj.handles.Spacing)*obj.handles.Spacing;
            xmin = floor(x / obj.handles.Spacing)*obj.handles.Spacing;
            if (cx-xmin) > (xmax-cx)
                cx=xmax;
            else
                cx=xmin;
            end
            
            ymax = ceil(y / obj.handles.Spacing)*obj.handles.Spacing;
            ymin = floor(y / obj.handles.Spacing)*obj.handles.Spacing;
            if (cy-ymin) > (ymax-cy)
                cy=ymax;
            else
                cy=ymin;
            end
            
        end
        
        function obj = ToggleGridPoint(obj,x,y)
            LoadBtnCallback(obj);
            disp(['ToggleGridPoint called']);
            xl = get(obj.handles.ImageAxes, 'xlim'); midX = mean(xl);
            yl = get(obj.handles.ImageAxes, 'ylim'); midY = mean(yl);
            %disp(['ToggleGridPoint called ' num2str(xl) ',' num2str(yl) ' -> ' num2str(cx) ',' num2str(cy)])
            
            if obj.handles.Data.NumGloms
                cur = GetCurrentGlom(obj,midX,midY);
                
                
                [cx cy] = findNearestXover(obj,x,y);
                
                
                disp(['ToggleGridPoint called ' num2str(x) ',' num2str(y) ' -> ' num2str(cx) ',' num2str(cy)])
                disp(['Current glom ' num2str(cur) ' label ' obj.handles.Data.Glom(cur).Label]);
                obj.handles.CurrentGlom=cur;
                
                if ~cur
                    disp('No current glom. Cannot use grid point function.')
                    return
                end
                
                %obj.handles.CurrentGlom
                num_tpoints = size(obj.handles.Data.Glom(cur).TuftGridPoints,1);
                num_bpoints = size(obj.handles.Data.Glom(cur).CapsuleGridPoints,1);
                
                disp('Current Tuft Points')
                for i = 1:num_tpoints
                    disp(['tuft ' num2str(i) '  ' num2str(obj.handles.Data.Glom(cur).TuftGridPoints(i,1)) ' , ' num2str(obj.handles.Data.Glom(cur).TuftGridPoints(i,1))]);
                end
                disp('Current Capsule Points')
                for i = 1:num_bpoints
                    disp(['tuft ' num2str(i) '  ' num2str(obj.handles.Data.Glom(cur).CapsuleGridPoints(i,1)) ' , ' num2str(obj.handles.Data.Glom(cur).CapsuleGridPoints(i,1))]);
                end
                
                
                flag=-1;
                count_bpoints=num_bpoints;
                while count_bpoints >= 1
                    if (obj.handles.Data.Glom(cur).CapsuleGridPoints(count_bpoints,1) == cx) && (obj.handles.Data.Glom(cur).CapsuleGridPoints(count_bpoints,2)==cy)
                        disp(['Point exists in capsule vector. Deleting point index ' num2str(count_bpoints)])
                        set(obj.handles.statusText, 'string', 'Deleting grid point.');
                        
                        obj.handles.Data.Glom(cur).CapsuleGridPoints(count_bpoints,:)=[];
                        flag=2;
                        
                        break
                    end
                    count_bpoints=count_bpoints-1;
                end
                if flag == -1
                    count_tpoints=num_tpoints;
                    while count_tpoints >= 1
                        if (obj.handles.Data.Glom(cur).TuftGridPoints(count_tpoints,1) == cx) && (obj.handles.Data.Glom(cur).TuftGridPoints(count_tpoints,2)==cy)
                            set(obj.handles.statusText, 'string', 'Toggle grid point from tuft to capsule list.');
                            disp(['Point exists in tuft vector.  Idx ' num2str(count_tpoints)])
                            flag=1;
                            obj.handles.Data.Glom(cur).TuftGridPoints(count_tpoints,:)=[];
                            
                            if (num_bpoints == 0)
                                obj.handles.Data.Glom(cur).CapsuleGridPoints=[cx cy];
                            else
                                obj.handles.Data.Glom(cur).CapsuleGridPoints(num_bpoints+1,:)=[cx cy];
                            end
                            break
                        end
                        count_tpoints=count_tpoints-1;
                        
                    end
                end
                % Point is not in Tuft or Capule, hence add new element in
                % Tufts
                if flag == -1
                    
                    if (num_tpoints == 0)
                        obj.handles.Data.Glom(cur).TuftGridPoints=[cx cy];
                    else
                        obj.handles.Data.Glom(cur).TuftGridPoints(num_tpoints+1,:)=[cx cy];
                    end
                    disp(['New point in tuft vector idx ' num2str(num_tpoints+1)]);
                    set(obj.handles.statusText, 'string', 'Adding new grid point.');
                    flag=0;
                end
                
                %             hold(obj.handles.ImageAxes,'on')
                %             switch flag
                %                 case 0
                %                     plot(obj.handles.ImageAxes,cx,cy,'or','MarkerSize',10);
                %                     set(obj.handles.statusText, 'string', 'Adding new grid point.');
                %                 case 1
                %                     %	      p = findobj(obj.handles.ImageAxes,'-depth',2,'xdata',cx,'ydata',cy);
                %                     %	      delete(p);
                %                     plot(obj.handles.ImageAxes,cx,cy,'og','MarkerSize',10,'LineWidth',2);
                %                     set(obj.handles.statusText, 'string', 'Toggle tuft to capsule grid point.');
                %                 case 2
                %                     p = findobj(obj.handles.ImageAxes,'-depth',2,'xdata',cx,'ydata',cy);
                %                     delete(p);
                %                     set(obj.handles.statusText, 'string', 'Deleting grid point.');
                %             end
                %             hold(obj.handles.ImageAxes,'off')
                
                
            end
            saveSlideDataStruct(obj);
            RefreshGridPoints(obj);
        end
        
        % least squares method to find nearest point to centre of ImageAxes
        function glomIndx = GetCurrentGlom(obj,X,Y)
            %LoadBtnCallback(obj);
            disp(['GetCurrentGlom called with X' num2str(X), ' Y ' num2str(Y) ])
            d = cat(1,obj.handles.Data.Glom.Location);
            e=repmat([X Y],size(d,1),1);
            f= sum((e-d).^2, 2);
            [~, glomIndx] =min(f);
            
            disp(['GetCurrentGlom output glom index ' num2str(glomIndx)])
            
            if glomIndx< 0 || glomIndx > obj.handles.Data.NumGloms
                disp(['GetCurrentGlom Error output glom index ' num2str(glomIndx)])
            end
            
        end
        
        function obj = RefreshGridPoints(obj)
            
            hLines=findobj(obj.handles.ImageAxes,'Type','line');
            delete(hLines);
            obj = loadSlideData(obj);
            if obj.handles.GridMode
                xl = get(obj.handles.ImageAxes, 'xlim'); midX = mean(xl);
                yl = get(obj.handles.ImageAxes, 'ylim'); midY = mean(yl);
                
                hold(obj.handles.ImageAxes,'on');
                obj = loadGlobalVariables(obj)
                %eval(obj.handles.inlineGlobalLoad)
                %obj.handles.dataset.PixelResolutionUM=1;
                %obj.handles.GridSpacingUM = 35;
                
                obj.handles.Spacing = floor(obj.handles.Data.GridSpacingUM * (1000 * obj.handles.DatasetVariables.PixelResolution));
                disp(['Spacing ' num2str(obj.handles.Spacing)]);
                % Xgrid
                xgrid_start = obj.handles.Spacing * ceil(xl(1) / obj.handles.Spacing);
                xgrid_end = obj.handles.Spacing * floor(xl(2) / obj.handles.Spacing);
                for xgrid = xgrid_start:obj.handles.Spacing:xgrid_end
                    
                    plot(obj.handles.ImageAxes,xgrid.*ones(1,numel(yl(1):yl(2))), yl(1):yl(2),'k','linewidth',1);
                end
                
                % Y grid
                ygrid_start = obj.handles.Spacing * ceil(yl(1) / obj.handles.Spacing);
                ygrid_end = obj.handles.Spacing * floor(yl(2) / obj.handles.Spacing);
                for ygrid = ygrid_start:obj.handles.Spacing:ygrid_end
                    
                    plot(obj.handles.ImageAxes, xl(1):xl(2),ygrid.*ones(1,numel(xl(1):xl(2))),'k','linewidth',1);
                end
                
                % Grid Points
                if obj.handles.Data.NumGloms
                    obj.handles.CurrentGlom = GetCurrentGlom(obj,midX,midY);
                    cur = obj.handles.CurrentGlom;
                    disp(['Nearest glom is ' num2str(cur) ' label ' obj.handles.Data.Glom(cur).Label ' at ' num2str(obj.handles.Data.Glom(cur).Location(1)) ','  num2str(obj.handles.Data.Glom(cur).Location(2))]);
                    
                    num_tpoints = size(obj.handles.Data.Glom(cur).TuftGridPoints,1);
                    num_bpoints = size(obj.handles.Data.Glom(cur).CapsuleGridPoints,1);
                    disp(['Num glom tuft points ' num2str(num_tpoints) '  Num glom capsule points '  num2str(num_bpoints) ]);
                    
                    for i=1:num_tpoints
                        plot(obj.handles.ImageAxes,...
                            obj.handles.Data.Glom(cur).TuftGridPoints(i,1),...
                            obj.handles.Data.Glom(cur).TuftGridPoints(i,2),...
                            'or','MarkerSize',10);
                    end
                    for i=1:num_bpoints
                        plot(obj.handles.ImageAxes,...
                            obj.handles.Data.Glom(cur).CapsuleGridPoints(i,1),...
                            obj.handles.Data.Glom(cur).CapsuleGridPoints(i,2),...
                            'og','MarkerSize',10,'LineWidth',2);
                    end
                    
                    hold(obj.handles.ImageAxes,'off');
                    set(obj.handles.statusText,'string','Refreshed grid and grid points.');
                end
                
                  % Set all lines and points' button-down to the image
                hLines=findobj(obj.handles.ImageAxes,'Type','line');
               set(hLines, 'buttondownfcn'   , @obj.winBtnDownFcn, ...
                        'interruptible'   , 'off', ...
                      'busyaction'      , 'queue')
                
                
            end
            %saveSlideDataStruct(obj);
        end
        
        function obj = ChangeLabel(obj,varargin)
            %  obj = loadSlideData(obj);
            try
                parentLoc=[get(gco,'xdata') get(gco,'ydata')];
                
                
                iglom = GetCurrentGlom(obj,parentLoc(1), parentLoc(2));
                disp(['ChangeLabel called Glom ' num2str(iglom) ])
                
                if iglom < 0 || iglom > obj.handles.Data.NumGloms
                    disp('Change Label out of range')
                    return
                else
                    disp('ChangeLabel index in range')
                end
                if isempty(obj.handles.Data.Glom(iglom).Label) || length(obj.handles.Data.Glom(iglom).Label) ~= 7
                    disp('Change Label current glom does not have a label.')
                    return
                else
                    disp('ChangeLabel label valid.')
                end
                MaxLabel = GetMaxLabel(obj);
                answer = inputdlg(['Change Label of Glom ' num2str(iglom) ' with current value ' obj.handles.Data.Glom(iglom).Label(2:end) '. The largest label in the current slide is ' num2str(MaxLabel) '. Numerical integer values only. '],...
                    'GlomViewer Change Label');
                
                
                if ~isempty(answer)
                    disp(['ChangeLabel processing user answer for new label: ' answer{1}])
                    
                    val = str2double(answer{1});  % Use curly bracket for subscript
                    if isnan(val)  || val < 1 || val > 999999 || ( ceil(val) ~= val )
                        % Handle empty value returned for unsuccessful conversion
                        % ...
                        errordlg('New glomeruli label number incorrect.', 'Xglom Error Dialog', 'modal')
                    else
                        disp(['ChangeLabel: correct new label ' num2str(val)] )
                        obj.handles.Data.Glom(iglom).Label=sprintf('#%06d',val);
                    end
                    
                end
                set(gco,'DisplayName', obj.handles.Data.Glom(iglom).Label)
                saveSlideDataStruct(obj);
                
            catch ME
                ME
            end
            %RefreshLabelEllipses(obj);
        end
        
        function obj = MoveGlom(obj,varargin)
            try
                parentLoc=[get(gco,'xdata') get(gco,'ydata')];
                
                obj.handles.CurrentGlom = GetCurrentGlom(obj,parentLoc(1), parentLoc(2));
                if obj.handles.CurrentGlom > 0
                    msgbox(['Press OK and then Left-click on new location in image to move glomeruli ' obj.handles.Data.Glom(obj.handles.CurrentGlom).Label '.'],'GlomViewer New Location','modal')
                    set (obj.handles.statusText,'string',[ 'Next left-click on image will give  new location for glomeruli ' obj.handles.Data.Glom(obj.handles.CurrentGlom).Label '  at position ' num2str(obj.handles.Data.Glom(obj.handles.CurrentGlom).Location)])
                    obj.handles.MovingLabel=1;
                    set(obj.handles.GlomViewer, 'Pointer', 'crosshair');
                    % obj.handles.Data.Glom(i).Location = [x y];
                else
                    disp(['MoveGlom: current glom not found. Current glom returned ' num2str(obj.handles.CurrentGlom)])
                end
                %             else
                %                 disp('MoveGlom: no input location.')
                %             end
            catch ME
                ME
            end
            saveSlideDataStruct(obj);
            %RefreshLabelEllipses(obj);
        end
        
        % DeleteGlom
        %   Content menu item for a glomeruli point that enables deletion.
        %   This calls the calling object 'gco' to get its location, then
        %   finds the nearest glom and deletes it.  Deletion of an element
        %   in an array of structs
        function obj=DeleteGlom(obj,varargin)
            %obj = loadSlideData(obj);
            set(obj.handles.statusText,'string','DeleteGlom called.');
            %ShowStatus('DeleteGlom called',handles)
            try
                %% Get the x y location of the calling object -- a single data point
                parentLoc=[get(gco,'xdata') get(gco,'ydata')];
                
                delete(gco)
                disp('DeleteGlom:  calling GetCurrentGlom')
                iglom = GetCurrentGlom(obj,parentLoc(1), parentLoc(2));
                
                disp(['DeleteGlom:  deleting glom ' num2str(iglom) ' found at location.']);
                if iglom > 0
                    disp(['DeleteGlom:  deleting glom ' num2str(iglom) ' found at location.']);
                    obj.handles.Data.Glom = obj.handles.Data.Glom(setdiff(1:obj.handles.Data.NumGloms, iglom));
                    obj.handles.Data.NumGloms = obj.handles.Data.NumGloms-1;
                    disp(['Length glom struct ' num2str(length(obj.handles.Data.Glom))  ' NumGloms ' num2str(obj.handles.Data.NumGloms)])
                    if obj.handles.Data.NumGloms == length(obj.handles.Data.Glom)
                        disp('DeleteGlom:  glom has been deleted.')
                    end
                    % saveSlideDataStruct(obj);
                    
                    if obj.handles.Data.NumGloms ~= length(obj.handles.Data.Glom)
                        disp('DeleteGlom:  glom has not been deleted after saving.')
                    end
                    
                end
            catch ME
            end
            %end
            %RefreshLabelEllipses(obj);
            RefreshDataTreeBox(obj);
            disp('DeleteGlom: closing.')
        end
        
        % RefreshLabelEllipses
        %   Update the visual blobs where the glomeruli are.
        function obj = RefreshLabelEllipses(obj,varargin)
            %obj = loadSlideDataMatFile(obj);
            hLines=findobj(obj.handles.ImageAxes,'Type','line');
            delete(hLines);
            
            %% TODO check for any ellipse handles and delete them as well
            
            
            % Adding imellipses to image
            % ~obj.handles.MovingLabel &&
            if ~obj.handles.GridMode && (obj.handles.Data.NumGloms > 0)
                
                
                %                     diam = floor(0.1 / obj.handles.DatasetVariables.PixelResolution)
                %                     rad = floor(diam/2.0)
                %                     disp(['Diam : ' num2str(diam) ',  Radius : ' num2str(rad) ])
                %                     if diam>1 && rad>1
                
                
                hold(obj.handles.ImageAxes,'on')
                %disp(['RefreshLabelEllipses:  num gloms ' num2str(obj.handles.Data.NumGloms)  ' size ' num2str(length(obj.handles.Data.Glom))])


                for ii=1:obj.handles.Data.NumGloms
                                       
                    
                    if ~isempty(obj.handles.Data.Glom(ii).Label)
                        %disp([' plotting point ' num2str(ii) '  ' obj.handles.Data.Glom(ii).Label])
                        try
                            figure(get(obj.handles.ImageAxes,'parent'))
                            hcmenu = uicontextmenu;   %('parent',obj.handles.ImageAxes);
                           % Define the context menu items and install their callbacks
                            item0 = uimenu(hcmenu, 'Label', obj.handles.Data.Glom(ii).Label);
                            
                            item1 = uimenu(hcmenu, 'Label', 'Change Label', 'Callback', {@obj.ChangeLabel, obj, obj.handles.Data.Glom(ii).Location} );
                            item2 = uimenu(hcmenu, 'Label', 'Delete', 'Callback', {@obj.DeleteGlom, obj, obj.handles.Data.Glom(ii).Location});
                            item3 = uimenu(hcmenu, 'Label', 'Move',  'Callback', {@obj.MoveGlom, obj, obj.handles.Data.Glom(ii).Location});
                            
                            disp(['Glom ' num2str(ii) ', x ' num2str(obj.handles.Data.Glom(ii).Location(1)) ' y ' num2str(obj.handles.Data.Glom(ii).Location(2))])
                            
                            x=obj.handles.Data.Glom(ii).Location(1);
                            y=obj.handles.Data.Glom(ii).Location(2);
                            
                            plot(obj.handles.ImageAxes,x,y,'ob',...
                                'MarkerSize',20,'LineWidth',4,...
                                'DisplayName',obj.handles.Data.Glom(ii).Label,...
                                 'buttondownfcn', @obj.winBtnDownFcn,...
                                 'uicontextmenu',hcmenu);
                            
                        catch ME
                            ME
                            disp(['Error caught in plotting glom label: ' num2str(ii)])
                        end
                        
                    else
                        disp([' Point ' num2str(ii) ' has no label '])
                    end
                    
                end
                
                %    end
                hold(obj.handles.ImageAxes,'off')
                set(obj.handles.statusText,'string','Refreshed glom labels.');
            end
            %RefreshDataTreeBox(obj);
            
        end
        
        function obj = RefreshBtnCallback(obj,varargin)
%            obj = loadSlideDataMatFile(obj);
            if obj.handles.GridMode
                RefreshGridPoints(obj);
            else
                RefreshLabelEllipses(obj);
            end
            RefreshDataTreeBox(obj);
            set(obj.handles.GridValueBox,'string',num2str(obj.handles.Data.GridSpacingUM));
            
        end
        
        function obj = AddNewGlom(obj,x,y)
%            obj=LoadBtnCallback(obj);
            % disp(obj.handles.DatasetVariables)
            % disp(['Pixel res (mm) : ' num2str(obj.handles.DatasetVariables.PixelResolution)])
            %   diam = floor(0.1 / obj.handles.DatasetVariables.PixelResolution)
            %   rad = floor(diam/2.0)
            %disp(['Diam : ' num2str(diam) ',  Radius : ' num2str(rad) ])
            %   if diam>1 && rad>1
            %    hEllipse = imellipse(obj.handles.ImageAxes,[x-rad y-rad diam diam])
            %   end
            
            
            
            obj.handles.Data.MaxLabel = obj.handles.Data.MaxLabel + 1;
            newlabelnum = GetMaxLabel(obj) + 1 ;
            
            %% Get new label
            new_glom_label = sprintf('#%06d',newlabelnum);
            disp(['make struct ' num2str(newlabelnum)])
            %if exist(obj.handles.Data.Glom(newlabelnum))
            %    clear obj.handles.Data.Glom(newlabelnum)
            %end
            %disp(obj.handles.Data)
            disp(obj.handles.Data.Glom)
            disp(['Adding new glom struct, current number ' num2str(obj.handles.Data.NumGloms)])
            obj.handles.Data.NumGloms = obj.handles.Data.NumGloms + 1;
            
            
            if newlabelnum == 1
                obj.handles.Data=rmfield(obj.handles.Data,'Glom');
                obj.handles.Data.Glom=struct('Label',new_glom_label,...
                    'Location' , [x y],...
                    'TuftGridPoints' , [],...
                    'CapsuleGridPoints' , []);
                %obj.handles.Data.Glom(1)=[];
            else
                obj.handles.Data.Glom(obj.handles.Data.NumGloms)=struct('Label',new_glom_label,...
                    'Location' , [x y],...
                    'TuftGridPoints' , [],...
                    'CapsuleGridPoints' , []);
            end
            ii=obj.handles.Data.NumGloms;
            set(obj.handles.statusText, 'string', 'New glom label added.')
            hcmenu = uicontextmenu;   %('parent',obj.handles.ImageAxes);
            % Define the context menu items and install their callbacks
            item0 = uimenu(hcmenu, 'Label', obj.handles.Data.Glom(ii).Label);
            
            item1 = uimenu(hcmenu, 'Label', 'Change Label', 'Callback', {@obj.ChangeLabel, obj, obj.handles.Data.Glom(ii).Location} );
            item2 = uimenu(hcmenu, 'Label', 'Delete', 'Callback', {@obj.DeleteGlom, obj, obj.handles.Data.Glom(ii).Location});
            item3 = uimenu(hcmenu, 'Label', 'Move',  'Callback', {@obj.MoveGlom, obj, obj.handles.Data.Glom(ii).Location});
            
            
            hold(obj.handles.ImageAxes,'on');
            plot(obj.handles.ImageAxes,x,y,'ob',...
                'MarkerSize',20,'LineWidth',4,...
                'DisplayName',new_glom_label,...
                'buttondownfcn'   , @obj.winBtnDownFcn,...
                'UIContextMenu',hcmenu);
            
            
            %figpos    = dsxy2figxy(obj.handles.ImageAxes, [x y],[x-10 y-10])
            %             [figx figy]= dsxy2figxy(obj.handles.ImageAxes, [x y],[x-10 y-10])
            %             annotation('textarrow',figx figy,...
            %                 'String',new_glom_label,...
            %                 'FitBoxToText','off',...
            %                 'FontSize',16,'FontWeight', 'bold','EdgeColor','none')%,'FaceAlpha',0)
            %
            hold(obj.handles.ImageAxes,'off');%drawnow;
            
            %saveSlideDataStruct(obj);
            RefreshDataTreeBox(obj);
            %RefreshLabelEllipses(obj);
        end
        
        
        function max_val = GetMaxLabel(obj)
%            obj = loadSlideDataMatFile(obj);
            max_val=-1;
            if obj.handles.Data.NumGloms==0 || numel(obj.handles.Data.Glom)==0
                max_val=0;
                
            else
                
                for ii = 1:obj.handles.Data.NumGloms
                    if numel(obj.handles.Data.Glom)>=ii && isstruct(obj.handles.Data.Glom(ii))
                        val = str2double( obj.handles.Data.Glom(ii).Label(2:end));
                        if val> max_val
                            max_val=val;
                        end
                    else
                        disp(['GetMaxLabel - glom struct does not exist ' num2str(ii)]);
                    end
                    
                end
            end
            disp(['Max label : ' num2str(max_val)])
        end
        
        %--------------------------------------------------------------------------
        % winBtnDownFcn
        %   This is called when the mouse is clicked in one of the axes
        %   NORMAL clicks will start panning mode.
        %   ALT clicks will start zooming mode.
        %   OPEN clicks will center the view.
        %--------------------------------------------------------------------------
        function obj = winBtnDownFcn(obj, hObj, varargin)
            
            stopTimer(obj);
%            obj = loadSlideDataMatFile(obj);
            set(obj.handles.MessageTextBox, 'visible', 'off');
            set(obj.handles.FileInfoBtn, 'value', false);
            
            if strcmp(get(hObj,'Type'),'line')
                 xy = get(get(hObj,'parent'), 'currentpoint')
      
                 %xy = get(obj.handles.ImageAxes, 'currentpoint');
            else
                 xy = get(hObj, 'currentpoint');
            end
            
            
            switch get(obj.handles.GlomViewer, 'selectiontype')
                case 'normal'
                    % Start panning mode
                    
                    if obj.handles.MovingLabel
                        
                      
                        disp(['MovingLabel active new point '  num2str(xy(1,1)) ',' num2str(xy(1,2))])
                        
                        obj.handles.Data.Glom(obj.handles.CurrentGlom).Location = [xy(1,1) xy(1,2)];
                        obj.handles.MovingLabel=0;
                        set(obj.handles.GlomViewer,    'pointer' ,'arrow')
                        saveSlideDataStruct(obj);
%                       obj = LoadBtnCallback(obj);
                        RefreshDataTreeBox(obj);
                        RefreshLabelEllipses(obj);
                        
                        
                    else
                        
                        set(obj.handles.GlomViewer, ...
                            'pointer'               , 'custom', ...
                            'pointershapecdata'     , GlomViewer.closedHandPointer, ...
                            'windowbuttonmotionfcn' , @winBtnMotionFcn);
                        set(obj.handles.GlomViewer, 'windowbuttonupfcn', @obj.winBtnUpFcn);
                    end
                case 'alt'
                    % Right click or ctrl-left click
                    % Start zooming mode
                    
                    xl = get(hObj, 'xlim'); midX = mean(xl); rngXhalf = diff(xl) / 2;
                    yl = get(hObj, 'ylim'); midY = mean(yl); rngYhalf = diff(yl) / 2;
                    curPt  = mean(xy);curPt = curPt(1:2);
                    curPt2 = (curPt-[midX, midY]) ./ [rngXhalf, rngYhalf];
                    curPt  = [curPt; curPt];
                    curPt2 = [-(1+curPt2).*[rngXhalf, rngYhalf];...
                        (1-curPt2).*[rngXhalf, rngYhalf]];
                    initPt = get(obj.handles.GlomViewer, 'currentpoint');
                    set(obj.handles.statusText, 'string', 'Zooming...');
                    set(obj.handles.GlomViewer, ...
                        'pointer'               , 'custom', ...
                        'pointershapecdata'     , GlomViewer.zoomInOutPointer, ...
                        'windowbuttonmotionfcn' , @zoomMotionFcn);
                    set(obj.handles.GlomViewer, 'windowbuttonupfcn', @obj.winBtnUpFcn);
                    
                case 'extend'
                    
                    if obj.handles.GridMode == 1
                        % XGlom - grid counting mode
                        obj=ToggleGridPoint(obj,xy(1,1), xy(1,2));
                    else
                        % XGlom Labelling mode
                        obj=AddNewGlom(obj,xy(1,1), xy(1,2));
                    end
%                    obj=LoadBtnCallback(obj);
                case 'open'
                    % Double Click
                    % Center the view
                    
                    set(obj.handles.GlomViewer, 'windowbuttonupfcn', @obj.winBtnUpFcn);
                    
                    % Get current units
                    un    = get(0, 'units');
                    set(0, 'units', 'pixels');
                    pt2   = get(0, 'pointerlocation');
                    pt    = xy;
                    axPos = get(obj.handles.ImageAxes, 'position');
                    xl = get(obj.handles.ImageAxes, 'xlim'); midX = mean(xl);
                    yl = get(obj.handles.ImageAxes, 'ylim'); midY = mean(yl);
                    
                    % update figure position in case it was moved
                    obj.handles.figPos = get(obj.handles.GlomViewer, 'position');
                    
                    % get distance between cursor and center of axes
                    d = norm(pt2 - (obj.handles.figPos(1:2) + axPos(1:2) + axPos(3:4)/2));
                    
                    if d > 2  % center only if distance is at least 2 pixels away
                        ld = (mean(pt(:, 1:2)) - [midX, midY]) / 10;
                        pd = ((obj.handles.figPos(1:2) + axPos(1:2) + axPos(3:4) / 2) - pt2) / 10;
                        
                        set(obj.handles.statusText, 'string', 'Centering...');
                        
                        % Animate with "good" speed
                        for id = [1, 4, 6.5, 7.8, 8.5, 9, 9.3, 9.6, 9.8, 10]
                            
                            % Set axes limits and automatically set ticks
                            % Set aspect ratios
                            set(obj.handles.ImageAxes, ...
                                'xlim'                , xl + id * ld(1), ...
                                'ylim'                , yl + id * ld(2), ...
                                'cameraviewanglemode' , 'auto', ...
                                'dataaspectratiomode' , 'auto', ...
                                'plotboxaspectratio'  , obj.handles.pbar);
                            
                            % Move pointer with limits
                            set(0, 'pointerlocation', pt2 + id * pd);
                            
                            pause(0.01);
                        end
                        
                    end
                    
                    % Reset UNITS
                    set(0, 'units', un);
                    
            end
            saveSlideDataStruct(obj);
            %RefreshBtnCallback(obj);
            
            %----------------------------------------------------------------------
            % winBtnMotionFcn (nested under winBtnDownFcn)
            %   This function is called when click-n-drag (panning) is happening
            %----------------------------------------------------------------------
            function winBtnMotionFcn(varargin)
                
                pt = get(obj.handles.ImageAxes, 'currentpoint');
                
                % Update axes limits and automatically set ticks
                % Set aspect ratios
                set(obj.handles.ImageAxes, ...
                    'xlim', get(obj.handles.ImageAxes, 'xlim') + (xy(1,1)-(pt(1,1)+pt(2,1))/2), ...
                    'ylim', get(obj.handles.ImageAxes, 'ylim') + (xy(1,2)-(pt(1,2)+pt(2,2))/2), ...
                    'cameraviewanglemode' , 'auto', ...
                    'dataaspectratiomode' , 'auto', ...
                    'plotboxaspectratio'  , obj.handles.pbar);
                set(obj.handles.statusText, 'string', 'Panning...');
                
            end
            
            
            %----------------------------------------------------------------------
            % zoomMotionFcn (nested under winBtnDownFcn)
            %   This performs the click-n-drag zooming function. The pointer
            %   location relative to the initial point determines the amount of
            %   zoom (in or out).
            %----------------------------------------------------------------------
            function zoomMotionFcn(hObj, varargin)
                
                % Power law allows for the inverse to work:
                %      C^(x) * C^(-x) = 1
                % Choose C to get "appropriate" zoom factor
                C                   = 50;
                pt                  = get(hObj, 'currentpoint');
                r                   = C ^ ((initPt(2) - pt(2)) / obj.handles.figPos(4));
                newLimSpan          = r * curPt2; dTemp = diff(newLimSpan);
                pt(1)               = initPt(1);
                
                % Determine new limits based on r
                lims                = curPt + newLimSpan;
                
                % Update axes limits and automatically set ticks
                % Set aspect ratios
                set(obj.handles.ImageAxes, ...
                    'xlim'                , lims(:,1), ...
                    'ylim'                , lims(:,2), ...
                    'cameraviewanglemode' , 'auto', ...
                    'dataaspectratiomode' , 'auto', ...
                    'plotboxaspectratio'  , obj.handles.pbar);
                
                % Update zoom indicator line
                set(obj.handles.ZoomLine, ...
                    'xdata', [initPt(1), pt(1)]/obj.handles.figPos(3), ...
                    'ydata', [initPt(2), pt(2)]/obj.handles.figPos(4));
                set(obj.handles.ZoomCaptionText, 'string', sprintf('%d %%', ...
                    round(diff(obj.handles.xlim100)/dTemp(1)*100)));
                
            end % zoomMotionFcn enclosed function
        end % winBtnDownFcn
        
        
        function obj = RefreshDataTreeBox(obj)
%            obj = loadSlideDataMatFile(obj);
            disp('Refreshing Data Text Box');
            empt_index=[];
            obj.handles.Data.NumGloms=length(obj.handles.Data.Glom);
            for ii= 1:obj.handles.Data.NumGloms
                if  isempty(obj.handles.Data.Glom(ii).Label)
                    disp(['DataTree Glom ' num2str(ii) ' does not have a label'])
                    if isempty(empt_index)
                        empt_index(1)=ii;
                    else
                        empt_index(end)=ii;
                    end
                end
            end
            if ~isempty(empt_index)
                obj.handles.Data.Glom(empt_index) =[];
            end
            obj.handles.Data.NumGloms=length(obj.handles.Data.Glom);
            for ii= 1:length(obj.handles.Data.Glom)
                if  isempty(obj.handles.Data.Glom(ii).Label)
                    disp(['DataTree Glom ' num2str(ii) ' still does not have a label'])
                    empt_index(end)=ii;
                end
            end
            obj.handles.Data.Glom(empt_index) =[];
            obj.handles.Data.NumGloms=length(obj.handles.Data.Glom);
            listelem = evalc('disp(obj.handles.Data)');
            listelem = regexp(listelem, '\n', 'split');
            listelem = regexprep(listelem,'^\s+','');
            
            for ii=1:6
                listelem{1,end}=[];
            end
            listindex = numel(listelem)+1;
            
            %listelem{1,listindex+obj.handles.Data.NumGloms}='';
            disp(['DataTree list size ' num2str(numel(listelem))]);
            listelem{1,listindex} = 'Label        Location       NumPoints: Tuft  Capsule';
            listindex=listindex+1;
            
            for ii= 1:obj.handles.Data.NumGloms
                if ~isempty(obj.handles.Data.Glom(ii).Label) || ~isempty(obj.handles.Data.Glom(ii).Location)
                    newstr = [obj.handles.Data.Glom(ii).Label '   ', ...
                        num2str(int32(obj.handles.Data.Glom(ii).Location(1)),'%d') ',' num2str(int32(obj.handles.Data.Glom(ii).Location(2)),'%d') , '              ', ...
                        num2str(size(obj.handles.Data.Glom(ii).TuftGridPoints,1),'%d'), '         ', ...
                        num2str(size(obj.handles.Data.Glom(ii).CapsuleGridPoints,1),'%d')];
                    listelem{1,listindex}=newstr;
                    listindex=listindex+1;
                else
                    disp(['DataTree Error Glom ' num2str(ii) '  does not have a label or location.'])
                end
            end
            
            %             for i= 1:obj.handles.Data.NumGloms
            %                 listelem{1,listindex}= ['    Glom(' num2str(i) ')  ' ];
            %                 listelem{1,listindex+1}= ['         Location: ' num2str(obj.handles.Data.Glom(i).Location(1)) ',' num2str(obj.handles.Data.Glom(i).Location(2)) ];
            %                 listelem{1,listindex+2}= ['         Num Tuft Points: ' num2str(size(obj.handles.Data.Glom(i).TuftGridPoints,1))];
            %                 listelem{1,listindex+3}= ['         Num Capsule Points: ' num2str(size(obj.handles.Data.Glom(i).CapsuleGridPoints,1))];
            %                 listindex=listindex+4;
            %             end
            
            %% clean empty elements in listelem
            empty_elements = find(cellfun(@isempty,listelem));
            if ~isempty(empty_elements)
                for ii=1:length(empty_elements)
                    listelem{1,empty_elements(ii)}=' ';
                end
            end
            
            %             disp('Showing variables')
            %             save('listelem.mat','listelem')
            %             listelem
            %             whos
            %             str = char(listelem);
            
            %
            
            set(obj.handles.DataTreeBox,'string',char(listelem),'value',1);
            % InitDataTreeBox(obj)
            % UpdateDataTreeBox(obj)
            
        end
        
        
        function scrollWheelFcn(obj, varargin)
            
            if obj.handles.GridMode == 1
                set(obj.handles.statusText, 'string', 'Zoom/Scrolling not allowed in Grid Mode.');
                return;
            end
            
            
            stopTimer(obj);
            
            if ~isempty(obj.handles.ImX)
                
                % Power law allows for the inverse to work:
                %      C^(x) * C^(-x) = 1
                % Choose C to get "appropriate" zoom factor
                C                   = 1.02;
                
                xl = get(obj.handles.ImageAxes, 'xlim'); midX = mean(xl); rngXhalf = diff(xl) / 2;
                yl = get(obj.handles.ImageAxes, 'ylim'); midY = mean(yl); rngYhalf = diff(yl) / 2;
                curPt  = mean(get(obj.handles.ImageAxes, 'currentpoint'));curPt = curPt(1:2);
                curPt2 = (curPt-[midX, midY]) ./ [rngXhalf, rngYhalf];
                curPt  = [curPt; curPt];
                curPt2 = [-(1+curPt2).*[rngXhalf, rngYhalf];...
                    (1-curPt2).*[rngXhalf, rngYhalf]];
                
                edata = varargin{2};
                r = C^(edata.VerticalScrollCount*edata.VerticalScrollAmount);
                newLimSpan          = r * curPt2;dTemp = diff(newLimSpan);
                
                % Determine new limits based on r
                lims                = curPt + newLimSpan;
                
                % Update axes limits and automatically set ticks
                % Set aspect ratios
                set(obj.handles.ImageAxes, ...
                    'xlim'                , lims(:,1), ...
                    'ylim'                , lims(:,2), ...
                    'cameraviewanglemode' , 'auto', ...
                    'dataaspectratiomode' , 'auto', ...
                    'plotboxaspectratio'  , obj.handles.pbar);
                
                % Update zoom value
                set(obj.handles.ZoomCaptionText, 'string', sprintf('%d %%', ...
                    round(diff(obj.handles.xlim100)/dTemp(1)*100)));
                
            end
            
            startTimer(obj);
            
        end
        
        %--------------------------------------------------------------------------
        % winBtnUpFcn
        %   This is called when the mouse is released
        %--------------------------------------------------------------------------
        function winBtnUpFcn(obj, hObj, varargin)
            
            set(hObj, ...
                'pointer'               , 'arrow', ...
                'windowbuttonmotionfcn' , '');
            set(obj.handles.statusText, 'string', '');
            set(obj.handles.ZoomLine, 'xdata', NaN, 'ydata', NaN);
            set(obj.handles.GlomViewer, 'windowbuttonupfcn', '');
            
            startTimer(obj);
            
        end
        
        %-------------------------------------------
        % gridBtnCallback
        %-------------------------------------------
        function obj=gridBtnCallback(obj,varargin)
            
            ToggleState = get(obj.handles.GridBtn,'Value');
            if ToggleState == 1
                set(obj.handles.GridBtn,'string','State: ON')
                obj.handles.GridMode = 1;
                
                RefreshGridPoints(obj);
            else
                
                obj.handles.GridMode = 0;
                hLines = findobj(obj.handles.ImageAxes,'Type','line');
                delete(hLines)
                set(obj.handles.GridBtn,'string','State: OFF')
                RefreshLabelEllipses(obj)
                %xl = get(obj.handles.ImageAxes, 'xlim');
                %yl = get(obj.handles.ImageAxes, 'ylim');
                %resizeFcn(obj);
            end
        end
        
        
        %--------------------------------------------------------------------------
        % startTimer
        %   This starts the timer. If the timer object is invalid, it creates a new
        %   one.
        %--------------------------------------------------------------------------
        function startTimer(obj)
            
            try
                
                if ~strcmpi(obj.handles.tm.Running, 'on');
                    start(obj.handles.tm);
                end
                
            catch %#ok<CTCH>
                
                obj.handles.tm          = timer(...
                    'name'            , 'image preview timer', ...
                    'executionmode'   , 'fixedspacing', ...
                    'objectvisibility', 'off', ...
                    'taskstoexecute'  , inf, ...
                    'period'          , 0.001, ...
                    'startdelay'      , 3, ...
                    'timerfcn'        , @obj.getPreviewImages);
                start(obj.handles.tm);
                
            end
            
        end
        
        
        %--------------------------------------------------------------------------
        % stopTimerFcn
        %   This gets called when the figure is closed.
        %--------------------------------------------------------------------------
        function obj=DeleteAllFcn(obj, varargin)
            
            stopTimerFcn(obj);
            saveSlideDataStruct(obj);
            set(obj.handles.figure1,  'closerequestfcn', '');
            delete(obj.handles.figure1);
            obj.handles=[];
        end
        
        %--------------------------------------------------------------------------
        % stopTimerFcn
        %   This gets called when the figure is closed.
        %--------------------------------------------------------------------------
        function obj=stopTimerFcn(obj, varargin)
            
            stop(obj.handles.tm);
            % wait until timer stops
            while ~strcmpi(obj.handles.tm.Running, 'off')
                drawnow;
            end
            delete(obj.handles.tm);
            
        end
        
        
        %--------------------------------------------------------------------------
        % stopTimer
        %   This stops the timer object used for generating image previews
        %--------------------------------------------------------------------------
        function obj=stopTimer(obj, varargin)
            
            stop(obj.handles.tm);
            
            % wait until timer stops
            while ~strcmpi(obj.handles.tm.Running, 'off')
                drawnow;
            end
            
            set(obj.handles.statusText, 'string', '');
            
        end
        
        
        %--------------------------------------------------------------------------
        % readImageFileFcn
        %   This function reads in the image file and converts to TRUECOLOR
        %--------------------------------------------------------------------------
        function [x, info] = readImageFileFcn(obj, filename)
            
            try
                [x, mp] = imread(filename);
                if size(x,3)==4
                    x = x(:,:,1:3);
                end
                info = imfinfo(filename);
                info = info(1);
                
                switch info.ColorType
                    case 'grayscale'
                        switch class(x)
                            case 'logical'
                                x = uint8(x);
                                mp = [0 0 0;1 1 1];
                                
                            case 'uint8'
                                mp = gray(256);
                                
                            case 'uint16'
                                mp = gray(2^16);
                                
                            case {'double','single'}
                                cmapsz = size(get(obj.handles.GlomViewer, 'Colormap'), 1);
                                mp = gray(cmapsz);
                                
                            case 'int16'
                                x = double(x)+2^15;
                                x = uint16((x-min(x(:)))/(max(x(:))-min(x(:)))*(2^16));
                                mp = gray(2^16);
                                
                            otherwise
                                cmapsz = size(get(obj.handles.GlomViewer, 'Colormap'), 1);
                                mp = gray(cmapsz);
                        end
                        x = ind2rgb(x, mp);
                        
                    case 'indexed'
                        if isempty(mp)
                            mp = info.Colormap;
                        end
                        x = ind2rgb(x, mp);
                        
                    otherwise
                end
                
            catch %#ok<CTCH>
                x = NaN;
                info = [];
                
            end
        end
        
        
        function obj = UpdateDataTreeBox(obj,varargin)
            
            % copy varargin contents to figure's handles
            sz_argin = size(varargin{:},2);
            
            struct_names = {};
            struct_values = {};
            for I = 1:sz_argin
                struct_names{I} = varargin{:}{1, I};
                struct_values{I} = varargin{:}{2, I};
                if isstruct(struct_values{I})
                    struct_names{I} = strcat('+', struct_names{I});
                end
            end
            %struct_names;
            % Populate the Structure listbox
            set(obj.handles.DataTreeBox,'string',struct_names)
            
            % save structures names and values to handles
            obj.handles.struct_names = struct_names';
            obj.handles.struct_values = struct_values';
            
            %guidata(hObject, handles);
            % update_listbox(handles)
            %set(obj.handles.DataTreeBox,'Value',[])
            
        end
        %----------
        % UpdateDataBox
        %-----
        function obj = InitDataBox(obj,varargin)
            
            clear ws_vars val cell_struct i L
            GlobalVariables=obj.handles.DatasetVariables;
            SlideData = obj.handles.Data;
            ws_vars = who('GlobalVariables','SlideData');
            
            L = length(ws_vars);
            cell_struct = {};
            for i=1:L
                val = eval(ws_vars{i});
                if isstruct(val)
                    cell_struct = [cell_struct, {ws_vars{i}; val}];
                end
            end
            
            disp(cell_struct)
            %set(obj.handles.DataTreeBox,'string',char(cell_struct))
            
        end
        
        
        
        %--------------------------------
        % ShowDataStructBtnCallback
        %  show data in datatreebox
        %--------------------------------
        function obj=ShowDataStructBtnCallback(obj,~,varargin)
            
            %SlideData = obj.handles.Data.Glom;
            evalc('disp(obj.handles.Data.Glom)');
            %set(obj.handles.DataTreeBox,'string',x);
            
            %cell_struct obj.handles.Data.Glom= InitDataBox(obj);
            %UpdateDataTreeBox(obj,cell_struct);
            
        end
        
        
        % --- Executes on selection change in listbox1.
        % Modified from StructBrowser
        function obj = DataTreeBoxCallback(obj, eventdata, handles)
            % hObject    handle to listbox1 (see GCBO)
            % eventdata  reserved - to be defined in a future version of MATLAB
            % handles    structure with handles and user data (see GUIDATA)
            
            % Hints: contents = get(hObject,'String') returns listbox1 contents as cell array
            %        contents{get(hObject,'Value')} returns selected item from listbox1
            % list_struct = get(hObject,'String');
            index_struct = get(obj.handles.DataTreeBox,'Value');
            struct_names = obj.handles.struct_names;
            struct_values = obj.handles.struct_values;
            
            indent = '   ';
            
            if size(index_struct,2) == 1   %%%%% % If one item is selected in the listbox %%%%%%%%%%%%%
                
                root_1 = struct_names{index_struct};
                is_indent = strfind(root_1, indent);
                if isempty(is_indent)
                    
                    level = 0;
                else
                    level = (is_indent(end) - 1)/7 + 1;
                end
                
                
                struct_val = struct_values{index_struct};
                %%%%%%
                struct_size = size(struct_val);
                
                if isstruct(struct_val)
                    fields =  fieldnames(struct_val);
                    for i = 1:length(fields)
                        if isstruct(getfield(struct_val(1), fields{i}))
                            fields{i} = strcat('+', fields{i});
                        end
                    end
                    set(handles.disp_struct_listbox,'String',fields)
                end
                
                info_value = whos('struct_val');
                if (isnumeric(struct_val) & (size(struct_val,1) * size(struct_val,2) )< 4096 )
                    if (size(struct_val,1) == 1 & size(struct_val,2) > 20)
                        struct_val = struct_val';
                    end
                    str_out = num2str(struct_val);
                    set(handles.disp_struct_listbox,'String',str_out)
                elseif ischar(struct_val)
                    set(handles.disp_struct_listbox,'String',struct_val)
                elseif(islogical(struct_val))
                    set(handles.disp_struct_listbox,'String',num2str(struct_val))
                elseif(~isstruct(struct_val))
                    set(handles.disp_struct_listbox,'String','value is not a numeric value or too big !!' )
                end
                
                sz1 = num2str(info_value.size(1));
                sz2 = num2str(info_value.size(2));
                btes = num2str(info_value.bytes);
                L1 = length(sz1);
                L2 = length(sz2);
                L3 = length(btes);
                
                string_info = [space(7,0), sz1, 'x',sz2, space(31,L1+L2+1),btes, space(30,L3) , info_value.class];
                set(handles.info_struc_text, 'string', string_info)
                
                % If double-click, and is struct, expand structure, and show fields
                
                
                if (strcmp(get(obj.handles.GlomViewer, 'SelectionType'), 'open')) % if double click
                    idxP = strfind(struct_names{ index_struct}, '+');
                    idxM = strfind(struct_names{ index_struct}, '-');
                    if ~isempty(idxP)
                        [struct_names, struct_values] = expand_struct(struct_names, struct_values, ...
                            index_struct, fields, level, idxP);
                    elseif  ~isempty(idxM)
                        [struct_names, struct_values] = shrink_struct(struct_names, struct_values, ...
                            index_struct, fields, level, idxM);
                    end
                    set(obj.handles.DataTreeBox,'String',struct_names);
                    obj.handles.struct_names = struct_names;
                    obj.handles.struct_values = struct_values;
                end
                
                
            end
            
            is_indent = [];
            for k = 1:length(struct_names)
                struct_name = struct_names{k};
                is_indent = [is_indent strfind(struct_name, indent)];
            end
            
            % Update handles structure
            guidata(obj, obj.handles);
            
            %--------------------------------
            %  Code below is from StructBrowser
            % check if cell elements are of class struct
            function cell_array = check_if_struct(cell_array);
                
                for i = 1:length(cell_array)
                    is_struct =  evalin('base',['isstruct(',cell_array{i},')']);
                    if is_struct
                        cell_array(i) = strcat('+   ', cell_array(i));
                    end
                end
            end
            
            
            function cell_array =  indent_cell(cell_array, level)
                
                indent = '       ';
                indent_app = [];
                for k = 1:level+1
                    indent_app = [indent_app indent];
                end
                for i=1:length(cell_array)
                    cell_array{i} = [indent_app cell_array{i}];
                end
            end
            
            
            % expand structure if '+' is double-clicked and update the structure tree
            function [struct_names, struct_values] = expand_struct(struct_names, struct_values, ...
                    idx, fields, level, idxP)
                
                size_val = size(struct_values{idx});
                if size_val(1) ~= 1
                    struct_values{idx} = (struct_values{idx})';
                end
                N = size_val(2);
                if N == 1 % if the structure is of size 1 x 1
                    names_be = struct_names(1:idx);
                    names_af = struct_names(idx + 1:length(struct_names));
                    names_app = indent_cell(fields, level);
                    values_be = struct_values(1:idx);
                    values_af = struct_values(idx + 1:length(struct_names));
                    for i = 1:length(fields)
                        if (fields{i}(1) == '+' || fields{i} == '-')
                            fields{i} = fields{i}(2:end);
                        end
                        values_app{i} = getfield(struct_values{idx}, fields{i});
                    end
                    
                    struct_names = [names_be; names_app; names_af];
                    struct_values = [values_be; values_app'; values_af];
                    struct_names{idx}(idxP) = '-';
                else% if the structure is of size 1 x N
                    names_be = struct_names(1:idx);
                    names_af = struct_names(idx + 1:length(struct_names));
                    names_app = cell(N,1);
                    values_app = cell(N,1);
                    struct_name = struct_names{idx};
                    struct_name = remove_indent2(struct_name);
                    for j = 1:N
                        names_app(j) = indent_cell(cellstr(strcat(struct_name,'(', num2str(j),')')), level);
                    end
                    values_be = struct_values(1:idx);
                    values_af = struct_values(idx + 1:length(struct_names));
                    for j = 1:N
                        values_app{j} = struct_values{idx}(j) ;
                    end
                    struct_names = [names_be; names_app; names_af];
                    struct_values = [values_be; values_app; values_af];
                    struct_names{idx}(idxP) = '-';
                end
            end
            
            % shrink structure if '-' is double-clicked
            function [struct_names, struct_values] = shrink_struct(struct_names, struct_values,...
                    idx, fields, level, idxM)
                struct_names{idx}(idxM) = '+';
                indent = '       ';
                if ((idxM-1)/7 - level) == 0
                    num_steps = 0;
                    is_indent_select = strfind(struct_names{idx}, indent);
                    if ~isempty(is_indent_select)
                        for k = idx+1 : length(struct_names)
                            is_indent = strfind(struct_names{k}, indent);
                            if (isempty(is_indent) || is_indent(end) - is_indent_select(end) <= 0)
                                break;
                            end
                            num_steps = num_steps + 1;
                        end
                    else
                        for k = idx+1 : length(struct_names)
                            is_indent = strfind(struct_names{k}, indent);
                            if isempty(is_indent)
                                break;
                            end
                            num_steps = num_steps + 1;
                        end
                    end
                else
                    num_steps = length(fields);
                end
                names_be = struct_names(1:idx);
                names_app = struct_names(idx+num_steps+1:length(struct_names));
                values_be = struct_values(1:idx);
                values_app = struct_values(idx+num_steps+1:length(struct_names));
                struct_names = [names_be; names_app];
                struct_values = [values_be; values_app];
            end
            % remove indent and '+' or '-' if exist before reading variable
            function str1 = remove_indent(str0)
                
                c = find(isspace(str0));
                if ~isempty(c)
                    str1 = str0(max(c)+1:end);
                else
                    str1 = str0;
                end
                
                if (str1(1) == '+' | str1(1) == '-')
                    str1 = str1(2:end);
                end
            end
            % remove indent keeping '+' and '-'
            function str1 = remove_indent2(str0)
                
                c = find(isspace(str0));
                if ~isempty(c)
                    str1 = str0(max(c)+1:end);
                else
                    str1 = str0;
                end
            end
            
            % generate space for information text
            function spc = space(L, prev_str)
                spc1 = ' ';
                spc = [];
                for i=1:L- prev_str
                    spc = [spc, spc1];
                end
            end
            
            
            
        end
        
    end
end



%--------------------------------------------------------------------------
% fixLongDirName
%   This truncates the directory string if it is too long to display
%--------------------------------------------------------------------------
function newdirname = fixLongDirName(dirname)
% Modify string for long directory names
if length(dirname) > 20
    [tmp1, tmp2] = strtok(dirname, filesep); %#ok<ASGLU>
    if isempty(tmp2)
        newdirname = dirname;
        
    else
        % in case the directory name starts with a file separator.
        id = strfind(dirname, tmp2);
        tmp1 = dirname(1:id(1));
        [p, tmp2] = fileparts(dirname);
        if strcmp(tmp1, p) || isempty(tmp2)
            newdirname = dirname;
            
        else
            newdirname = fullfile(tmp1, '...', tmp2);
            tmp3 = '';
            while length(newdirname) < 20
                tmp3 = fullfile(tmp2, tmp3);
                [p, tmp2] = fileparts(p);
                if strcmp(tmp1, p)  % reach root directory
                    newdirname = dirname;
                    %break; % it will break because dirname is longer than 30 chars
                    
                else
                    newdirname = fullfile(tmp1, '...', tmp2, tmp3);
                    
                end
            end
        end
    end
else
    newdirname = dirname;
end

end



%--------------------------------------------------------------------------
% helpBtnCallback
%   This opens up a help dialog box
%--------------------------------------------------------------------------
function helpBtnCallback(varargin)

helpdlg({...
    'Navigate through directories using the file list box on the left.', ...
    '   Single-click to see the preview.', ...
    '   Double-click (list box OR preview image) to open and display image.', ...
    'Image', '   Click and drag to pan the image.', ...
    '   Right click and drag to zoom. (or scroll wheel if R2007a or later)', ...
    '   Double-click to center view.', ...
    '''Full'' displays the whole image.', ...
    '''Custom %'' displays the image at the true 100% resolution at default. Right click the button to change the zoom scale.', ...
    '''File Info'' displays the current image file info.',...
    '',...
    'GlomViewer Workflow',...
    'With grid off:',...
    '   Shift-Left click in image to add glomeruli labels (blue circles should appear)',...
    '   Right-click on a blue circle shows options for glomeruli (change label, delete, move)',...
    'With grid on:',...
    '   Shift-Left click toggles the nearest cross-over with:',...
    '        Red circle:  Glomerular Tuft ',...
    '        Green circle:  Bowman''s Capsule',...
    '        Empty: Crossover deleted ',...
    '',...
    'Copyright © 2012-2013 Michael Eager <michael.eager@monash.edu> and',...
    'Monash Biomedical Imaging, Monash University, Australia',...
    '',...
    'GlomViewer is part of Xglom (https://code.google.com/p/xglom/).',...
    ' ',...
    'This is free software: you can redistribute it and/or modify',...
    'it under the terms of the GNU General Public License as published by',...
    'the Free Software Foundation, either version 3 of the License, or',...
    '(at your option) any later version.'}, 'Help');

end


